From b46b8613833cca4233d95d0ea15c326dc88e4444 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 18:54:33 -0200 Subject: [PATCH 01/52] Removed unsed variable. --- cake/libs/cake_socket.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cake/libs/cake_socket.php b/cake/libs/cake_socket.php index 3bcb7ca0a..e07b9837f 100644 --- a/cake/libs/cake_socket.php +++ b/cake/libs/cake_socket.php @@ -112,7 +112,6 @@ class CakeSocket { } if ($this->config['persistent'] == true) { - $tmp = null; $this->connection = @pfsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']); } else { $this->connection = @fsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']); From fb5495fad55c6590f1673af9616ee1109c94e6da Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 18:58:29 -0200 Subject: [PATCH 02/52] Throws an exception on fail in connect. --- cake/libs/cake_socket.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cake/libs/cake_socket.php b/cake/libs/cake_socket.php index e07b9837f..612d42e21 100644 --- a/cake/libs/cake_socket.php +++ b/cake/libs/cake_socket.php @@ -100,6 +100,7 @@ class CakeSocket { * Connect the socket to the given host and port. * * @return boolean Success + * @throws Exception */ public function connect() { if ($this->connection != null) { @@ -119,6 +120,7 @@ class CakeSocket { if (!empty($errNum) || !empty($errStr)) { $this->setLastError($errStr, $errNum); + throw new Exception($errStr, $errNum); } $this->connected = is_resource($this->connection); From 8c29847c8f262406879a3483860434e9d015203d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 19:07:29 -0200 Subject: [PATCH 03/52] Simplified the CakeSocket and fixed some phpdocs. --- cake/libs/cake_socket.php | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/cake/libs/cake_socket.php b/cake/libs/cake_socket.php index 612d42e21..531c3b43a 100644 --- a/cake/libs/cake_socket.php +++ b/cake/libs/cake_socket.php @@ -138,9 +138,8 @@ class CakeSocket { public function host() { if (Validation::ip($this->config['host'])) { return gethostbyaddr($this->config['host']); - } else { - return gethostbyaddr($this->address()); } + return gethostbyaddr($this->address()); } /** @@ -151,9 +150,8 @@ class CakeSocket { public function address() { if (Validation::ip($this->config['host'])) { return $this->config['host']; - } else { - return gethostbyname($this->config['host']); } + return gethostbyname($this->config['host']); } /** @@ -164,9 +162,8 @@ class CakeSocket { public function addresses() { if (Validation::ip($this->config['host'])) { return array($this->config['host']); - } else { - return gethostbynamel($this->config['host']); } + return gethostbynamel($this->config['host']); } /** @@ -177,9 +174,8 @@ class CakeSocket { public function lastError() { if (!empty($this->lastError)) { return $this->lastError['num'] . ': ' . $this->lastError['str']; - } else { - return null; } + return null; } /** @@ -187,6 +183,7 @@ class CakeSocket { * * @param integer $errNum Error code * @param string $errStr Error string + * @return void */ public function setLastError($errNum, $errStr) { $this->lastError = array('num' => $errNum, 'str' => $errStr); @@ -230,17 +227,8 @@ class CakeSocket { return false; } return $buffer; - } else { - return false; } - } - -/** - * Abort socket operation. - * - * @return boolean Success - */ - public function abort() { + return false; } /** @@ -273,6 +261,7 @@ class CakeSocket { /** * Resets the state of this Socket instance to it's initial state (before Object::__construct got executed) * + * @param array $state Array with key and values to reset * @return boolean True on success */ public function reset($state = null) { From 2bed6622fec146234ead490323485b256d792b6b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 21:00:57 -0200 Subject: [PATCH 04/52] Added tests to thown in connect. --- cake/tests/cases/libs/cake_socket.test.php | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cake/tests/cases/libs/cake_socket.test.php b/cake/tests/cases/libs/cake_socket.test.php index 48b304060..c14afbff6 100644 --- a/cake/tests/cases/libs/cake_socket.test.php +++ b/cake/tests/cases/libs/cake_socket.test.php @@ -101,6 +101,30 @@ class CakeSocketTest extends CakeTestCase { $this->assertTrue($this->Socket->connected); } +/** + * data provider function for testInvalidConnection + * + * @return array + */ + public static function invalidConnections() { + return array( + array(array('host' => 'invalid.host')), + array(array('host' => '127.0.0.1', 'port' => '70000')) + ); + } + +/** + * testInvalidConnection method + * + * @dataProvider invalidConnections + * @expectedException Exception + * return void + */ + public function testInvalidConnection($data) { + $this->Socket->config = array_merge($this->Socket->config, $data); + $this->Socket->connect(); + } + /** * testSocketHost method * From c6dd77de6f778dd7b4d7e48e7f78067336f1fcba Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 21:04:52 -0200 Subject: [PATCH 05/52] Removed unsed attribute. --- cake/libs/http_socket.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 9848446c4..2a2c6333e 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -30,14 +30,6 @@ App::import('Core', array('CakeSocket', 'Set', 'Router')); */ class HttpSocket extends CakeSocket { -/** - * Object description - * - * @var string - * @access public - */ - public $description = 'HTTP-based DataSource Interface'; - /** * When one activates the $quirksMode by setting it to true, all checks meant to * enforce RFC 2616 (HTTP/1.1 specs). From 8a4faa1e6921c5853507bd0be525bf5aaa063deb Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 21:12:40 -0200 Subject: [PATCH 06/52] Updated doc to avoid E_STRICT messages. --- cake/libs/http_socket.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 2a2c6333e..3dea7ad28 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -138,12 +138,12 @@ class HttpSocket extends CakeSocket { * You can use a url string to set the url and use default configurations for * all other options: * - * `$http =& new HttpSockect('http://cakephp.org/');` + * `$http = new HttpSockect('http://cakephp.org/');` * * Or use an array to configure multiple options: * * {{{ - * $http =& new HttpSocket(array( + * $http = new HttpSocket(array( * 'host' => 'cakephp.org', * 'timeout' => 20 * )); From ef2a7d4608e5f531775d4b8fc5d77403858ca833 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 21:28:11 -0200 Subject: [PATCH 07/52] Replaced trigger_error by throw Exception. --- cake/libs/http_socket.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 3dea7ad28..b25020530 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -532,6 +532,7 @@ class HttpSocket extends CakeSocket { * * @param string $body A string continaing the chunked body to decode. * @return mixed Array of response headers and body or false. + * @throws Exception */ protected function _decodeChunkedBody($body) { if (!is_string($body)) { @@ -544,8 +545,7 @@ class HttpSocket extends CakeSocket { while ($chunkLength !== 0) { if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) { if (!$this->quirksMode) { - trigger_error(__('HttpSocket::_decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.'), E_USER_WARNING); - return false; + throw new Exception(__('HttpSocket::_decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.')); } break; } @@ -794,6 +794,7 @@ class HttpSocket extends CakeSocket { * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET. * @param string $versionToken The version token to use, defaults to HTTP/1.1 * @return string Request line + * @throws Exception */ protected function _buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { $asteriskMethods = array('OPTIONS'); @@ -801,8 +802,7 @@ class HttpSocket extends CakeSocket { if (is_string($request)) { $isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match); if (!$this->quirksMode && (!$isValid || ($match[2] == '*' && !in_array($match[3], $asteriskMethods)))) { - trigger_error(__('HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.'), E_USER_WARNING); - return false; + throw new Exception(__('HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.')); } return $request; } elseif (!is_array($request)) { @@ -816,8 +816,7 @@ class HttpSocket extends CakeSocket { $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query'); if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { - trigger_error(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.'), join(',', $asteriskMethods)), E_USER_WARNING); - return false; + throw new Exception(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.'), join(',', $asteriskMethods))); } return $request['method'].' '.$request['uri'].' '.$versionToken.$this->lineBreak; } From 777afb6d3ebf201b06a27b77c0ba5c594ee87f8e Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 9 Nov 2010 23:03:43 -0200 Subject: [PATCH 08/52] separeted auth from HttpSocket. Refs #880 --- cake/libs/http/basic_method.php | 42 ++++++++++++++++ cake/libs/http_socket.php | 32 +++++++++--- .../cases/libs/http/basic_method.test.php | 49 +++++++++++++++++++ cake/tests/cases/libs/http_socket.test.php | 3 ++ 4 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 cake/libs/http/basic_method.php create mode 100644 cake/tests/cases/libs/http/basic_method.test.php diff --git a/cake/libs/http/basic_method.php b/cake/libs/http/basic_method.php new file mode 100644 index 000000000..da7951c6c --- /dev/null +++ b/cake/libs/http/basic_method.php @@ -0,0 +1,42 @@ +request['auth']['user'], $http->request['auth']['pass'])) { + $http->request['header']['Authorization'] = 'Basic ' . base64_encode($http->request['auth']['user'] . ':' . $http->request['auth']['pass']); + } + } + +} diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index b25020530..3def34cea 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -236,12 +236,7 @@ class HttpSocket extends CakeSocket { $this->request['header'] = array_merge(compact('Host'), $this->request['header']); } - if (isset($this->request['auth']['user']) && isset($this->request['auth']['pass'])) { - $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['auth']['user'] . ":" . $this->request['auth']['pass']); - } - if (isset($this->request['uri']['user']) && isset($this->request['uri']['pass'])) { - $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['uri']['user'] . ":" . $this->request['uri']['pass']); - } + $this->_setAuth(); if (is_array($this->request['body'])) { $this->request['body'] = $this->_httpSerialize($this->request['body']); @@ -439,6 +434,31 @@ class HttpSocket extends CakeSocket { return $this->_buildUri($url); } +/** + * Set authentication in request + * + * @return void + * @throws Exception + */ + protected function _setAuth() { + if (empty($this->request['auth']['method'])) { + if (isset($this->request['uri']['user'], $this->request['uri']['pass'])) { + $this->request['auth'] = array( + 'method' => 'Basic', + 'user' => $this->request['uri']['user'], + 'pass' => $this->request['uri']['pass'] + ); + } else { + return; + } + } + $authClass = Inflector::camelize($this->request['auth']['method']) . 'Method'; + if (!App::import('Lib', 'http/' . $authClass)) { + throw new Exception(__('Authentication method unknown.')); + } + $authClass::authentication($this); + } + /** * Parses the given message and breaks it down in parts. * diff --git a/cake/tests/cases/libs/http/basic_method.test.php b/cake/tests/cases/libs/http/basic_method.test.php new file mode 100644 index 000000000..c9d487f70 --- /dev/null +++ b/cake/tests/cases/libs/http/basic_method.test.php @@ -0,0 +1,49 @@ + + * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.http + * @since CakePHP(tm) v 2.0.0 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +App::import('Core', 'HttpSocket'); +App::import('Lib', 'http/BasicMethod'); + +/** + * BasicMethodTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.http + */ +class BasicMethodTest extends CakeTestCase { + +/** + * testAuthentication method + * + * @return void + */ + public function testAuthentication() { + $http = new HttpSocket(); + $http->request['auth'] = array( + 'method' => 'Basic', + 'user' => 'mark', + 'pass' => 'secret' + ); + + BasicMethod::authentication($http); + $this->assertEqual($http->request['header']['Authorization'], 'Basic bWFyazpzZWNyZXQ='); + } + +} \ No newline at end of file diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 5b3f79e0c..beb0fc0c5 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -694,14 +694,17 @@ class HttpSocketTest extends CakeTestCase { $socket->get('http://mark:secret@example.com/test'); $this->assertEqual($socket->request['uri']['user'], 'mark'); $this->assertEqual($socket->request['uri']['pass'], 'secret'); + $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); $socket->get('/test2'); $this->assertEqual($socket->request['auth']['user'], 'mark'); $this->assertEqual($socket->request['auth']['pass'], 'secret'); + $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); $socket->get('/test3'); $this->assertEqual($socket->request['auth']['user'], 'mark'); $this->assertEqual($socket->request['auth']['pass'], 'secret'); + $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); } /** From 1dfd3ce0b17568ca351322a2b49532998532685e Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 13 Nov 2010 13:40:00 -0200 Subject: [PATCH 09/52] Changing the auth value only if auth not is setted. --- cake/libs/http_socket.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 3def34cea..7a595364e 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -442,7 +442,7 @@ class HttpSocket extends CakeSocket { */ protected function _setAuth() { if (empty($this->request['auth']['method'])) { - if (isset($this->request['uri']['user'], $this->request['uri']['pass'])) { + if (isset($this->request['uri']['user'], $this->request['uri']['pass']) && !isset($this->request['auth']['user'])) { $this->request['auth'] = array( 'method' => 'Basic', 'user' => $this->request['uri']['user'], From 1d56625f3a243e90733eb864fd2f4572abf6c713 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 13 Nov 2010 17:21:52 -0200 Subject: [PATCH 10/52] If method is false, dont send the authentication. Fixed throw message. --- cake/libs/http_socket.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 7a595364e..a903aa445 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -441,6 +441,9 @@ class HttpSocket extends CakeSocket { * @throws Exception */ protected function _setAuth() { + if ($this->request['auth']['method'] === false) { + return; + } if (empty($this->request['auth']['method'])) { if (isset($this->request['uri']['user'], $this->request['uri']['pass']) && !isset($this->request['auth']['user'])) { $this->request['auth'] = array( @@ -454,7 +457,7 @@ class HttpSocket extends CakeSocket { } $authClass = Inflector::camelize($this->request['auth']['method']) . 'Method'; if (!App::import('Lib', 'http/' . $authClass)) { - throw new Exception(__('Authentication method unknown.')); + throw new Exception(__('Unknown authentication method.')); } $authClass::authentication($this); } From 44629bd673e90ab65f42b871ae73c961ebac4118 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 13 Nov 2010 20:51:09 -0200 Subject: [PATCH 11/52] Support to digest method in HttpSocket. Thanks to jmcneese and Adrien Gibrat. --- cake/libs/http/digest_method.php | 103 ++++++++++ .../cases/libs/http/digest_method.test.php | 182 ++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 cake/libs/http/digest_method.php create mode 100644 cake/tests/cases/libs/http/digest_method.test.php diff --git a/cake/libs/http/digest_method.php b/cake/libs/http/digest_method.php new file mode 100644 index 000000000..42d0c9482 --- /dev/null +++ b/cake/libs/http/digest_method.php @@ -0,0 +1,103 @@ +request['auth']['user'], $http->request['auth']['pass'])) { + if (!isset($http->config['request']['auth']['realm']) && !self::_getServerInformation($http)) { + return; + } + $http->request['header']['Authorization'] = self::_generateHeader($http); + } + } + +/** + * Retrive information about the authetication + * + * @param HttpSocket $http + * @return boolean + */ + protected static function _getServerInformation(&$http) { + $originalRequest = $http->request; + $http->request['auth'] = array('method' => false); + $http->request($http->request); + $http->request = $originalRequest; + + if (empty($http->response['header']['Www-Authenticate'])) { + return false; + } + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $http->response['header']['Www-Authenticate'], $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $http->config['request']['auth'][$match[1]] = $match[2]; + } + if (!empty($http->config['request']['auth']['qop']) && empty($http->config['request']['auth']['nc'])) { + $http->config['request']['auth']['nc'] = 1; + } + return true; + } + +/** + * Generate the header Authorization + * + * @param HttpSocket $http + * @return string + */ + protected static function _generateHeader(&$http) { + $a1 = md5($http->request['auth']['user'] . ':' . $http->config['request']['auth']['realm'] . ':' . $http->request['auth']['pass']); + $a2 = md5($http->request['method'] . ':' . $http->request['uri']['path']); + + if (empty($http->config['request']['auth']['qop'])) { + $response = md5($a1 . ':' . $http->config['request']['auth']['nonce'] . ':' . $a2); + } else { + $http->config['request']['auth']['cnonce'] = uniqid(); + $nc = sprintf('%08x', $http->config['request']['auth']['nc']++); + $response = md5($a1 . ':' . $http->config['request']['auth']['nonce'] . ':' . $nc . ':' . $http->config['request']['auth']['cnonce'] . ':auth:' . $a2); + } + + $authHeader = 'Digest '; + $authHeader .= 'username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $http->request['auth']['user']) . '", '; + $authHeader .= 'realm="' . $http->config['request']['auth']['realm'] . '", '; + $authHeader .= 'nonce="' . $http->config['request']['auth']['nonce'] . '", '; + $authHeader .= 'uri="' . $http->request['uri']['path'] . '", '; + $authHeader .= 'response="' . $response . '"'; + if (!empty($http->config['request']['auth']['opaque'])) { + $authHeader .= ', opaque="' . $http->config['request']['auth']['opaque'] . '"'; + } + if (!empty($http->config['request']['auth']['qop'])) { + $authHeader .= ', qop="auth", nc=' . $nc . ', cnonce="' . $http->config['request']['auth']['cnonce'] . '"'; + } + return $authHeader; + } +} diff --git a/cake/tests/cases/libs/http/digest_method.test.php b/cake/tests/cases/libs/http/digest_method.test.php new file mode 100644 index 000000000..c5a67542b --- /dev/null +++ b/cake/tests/cases/libs/http/digest_method.test.php @@ -0,0 +1,182 @@ + + * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.http + * @since CakePHP(tm) v 2.0.0 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +App::import('Core', 'HttpSocket'); +App::import('Lib', 'http/DigestMethod'); + +class DigestHttpSocket extends HttpSocket { + +/** + * nextHeader attribute + * + * @var string + */ + public $nextHeader = ''; + +/** + * request method + * + * @param mixed $request + * @return void + */ + public function request($request) { + $this->response['header']['Www-Authenticate'] = $this->nextHeader; + } + +} + +/** + * DigestMethodTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.http + */ +class DigestMethodTest extends CakeTestCase { + +/** + * Socket property + * + * @var mixed null + */ + public $HttpSocket = null; + +/** + * This function sets up a HttpSocket instance we are going to use for testing + * + * @return void + */ + public function setUp() { + $this->HttpSocket = new DigestHttpSocket(); + $this->HttpSocket->request['method'] = 'GET'; + $this->HttpSocket->request['uri']['path'] = '/'; + $this->HttpSocket->request['auth'] = array( + 'method' => 'Digest', + 'user' => 'admin', + 'pass' => '1234' + ); + } + +/** + * We use this function to clean up after the test case was executed + * + * @return void + */ + function tearDown() { + unset($this->HttpSocket); + } + +/** + * testBasic method + * + * @return void + */ + public function testBasic() { + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; + $this->HttpSocket->config['request']['auth'] = array(); + $this->assertFalse(isset($this->HttpSocket->request['header']['Authorization'])); + DigestMethod::authentication($this->HttpSocket); + $this->assertTrue(isset($this->HttpSocket->request['header']['Authorization'])); + $this->assertEqual($this->HttpSocket->config['request']['auth']['realm'], 'The batcave'); + $this->assertEqual($this->HttpSocket->config['request']['auth']['nonce'], '4cded326c6c51'); + } + +/** + * testQop method + * + * @return void + */ + public function testQop() { + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestMethod::authentication($this->HttpSocket); + $expected = 'Digest username="admin", realm="The batcave", nonce="4cded326c6c51", uri="/", response="da7e2a46b471d77f70a9bb3698c8902b"'; + $this->assertEqual($expected, $this->HttpSocket->request['header']['Authorization']); + $this->assertFalse(isset($this->HttpSocket->config['request']['auth']['qop'])); + $this->assertFalse(isset($this->HttpSocket->config['request']['auth']['nc'])); + + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",qop="auth"'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestMethod::authentication($this->HttpSocket); + $expected = '@Digest username="admin", realm="The batcave", nonce="4cded326c6c51", uri="/", response="[a-z0-9]{32}", qop="auth", nc=00000001, cnonce="[a-z0-9]+"@'; + $this->assertPattern($expected, $this->HttpSocket->request['header']['Authorization']); + $this->assertEqual($this->HttpSocket->config['request']['auth']['qop'], 'auth'); + $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 2); + } + +/** + * testOpaque method + * + * @return void + */ + public function testOpaque() { + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestMethod::authentication($this->HttpSocket); + $this->assertFalse(strpos($this->HttpSocket->request['header']['Authorization'], 'opaque="d8ea7aa61a1693024c4cc3a516f49b3c"')); + + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",opaque="d8ea7aa61a1693024c4cc3a516f49b3c"'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestMethod::authentication($this->HttpSocket); + $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'opaque="d8ea7aa61a1693024c4cc3a516f49b3c"') > 0); + } + +/** + * testMultipleRequest method + * + * @return void + */ + public function testMultipleRequest() { + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",qop="auth"'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestMethod::authentication($this->HttpSocket); + $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000001') > 0); + $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 2); + + DigestMethod::authentication($this->HttpSocket); + $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000002') > 0); + $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 3); + $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); + $response = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); + + $this->HttpSocket->nextHeader = ''; + DigestMethod::authentication($this->HttpSocket); + $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000003') > 0); + $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 4); + $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); + $response2 = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); + $this->assertNotEqual($response, $response2); + } + +/** + * testPathChanged method + * + * @return void + */ + public function testPathChanged() { + $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; + $this->HttpSocket->request['uri']['path'] = '/admin'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestMethod::authentication($this->HttpSocket); + $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); + $response = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); + $this->assertNotEqual($response, 'da7e2a46b471d77f70a9bb3698c8902b'); + } + +} \ No newline at end of file From 85607a9963ea375dc4d01bd098f8dbdcaa051f8b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 13 Nov 2010 21:44:11 -0200 Subject: [PATCH 12/52] Removed interference in the name of the headers. --- cake/libs/http_socket.php | 28 ++++++++++------------ cake/tests/cases/libs/http_socket.test.php | 14 +++++------ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index a903aa445..0df3a63e1 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -864,6 +864,7 @@ class HttpSocket extends CakeSocket { * Builds the header. * * @param array $header Header to build + * @param string $mode * @return string Header built from array */ protected function _buildHeader($header, $mode = 'standard') { @@ -873,6 +874,17 @@ class HttpSocket extends CakeSocket { return false; } + $fieldsInHeader = array(); + foreach ($header as $key => $value) { + $lowKey = strtolower($key); + if (array_key_exists($lowKey, $fieldsInHeader)) { + $header[$fieldsInHeader[$lowKey]] = $value; + unset($header[$key]); + } else { + $fieldsInHeader[$lowKey] = $key; + } + } + $returnHeader = ''; foreach ($header as $field => $contents) { if (is_array($contents) && $mode == 'standard') { @@ -896,16 +908,6 @@ class HttpSocket extends CakeSocket { */ protected function _parseHeader($header) { if (is_array($header)) { - foreach ($header as $field => $value) { - unset($header[$field]); - $field = strtolower($field); - preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE); - - foreach ($offsets[0] as $offset) { - $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1); - } - $header[$field] = $value; - } return $header; } elseif (!is_string($header)) { return false; @@ -922,12 +924,6 @@ class HttpSocket extends CakeSocket { $field = $this->_unescapeToken($field); - $field = strtolower($field); - preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE); - foreach ($offsets[0] as $offset) { - $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1); - } - if (!isset($header[$field])) { $header[$field] = $value; } else { diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index beb0fc0c5..fe6ec1c70 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -529,7 +529,7 @@ class HttpSocketTest extends CakeTestCase { $expectation['request']['raw'] = $expectation['request']['line'].$expectation['request']['header']."\r\n".$raw; $r = array('config' => $this->Socket->config, 'request' => $this->Socket->request); - $v = $this->assertEquals($r, $expectation, '%s in test #'.$i.' '); + $v = $this->assertEquals($r, $expectation, 'Failed test #'.$i.' '); $expectation['request']['raw'] = $raw; } @@ -948,13 +948,13 @@ class HttpSocketTest extends CakeTestCase { $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; $r = $this->Socket->decodeChunkedBody($encoded); $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], array('Foo-Header' => 'bar', 'Cake' => 'PHP')); + $this->assertEquals($r['header'], array('foo-header' => 'bar', 'cake' => 'PHP')); $this->Socket->quirksMode = true; $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; $r = $this->Socket->decodeChunkedBody($encoded); $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], array('Foo-Header' => 'bar', 'Cake' => 'PHP')); + $this->assertEquals($r['header'], array('foo-header' => 'bar', 'cake' => 'PHP')); $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n"; $r = $this->Socket->decodeChunkedBody($encoded); @@ -1393,7 +1393,7 @@ class HttpSocketTest extends CakeTestCase { $this->Socket->reset(); $r = $this->Socket->parseHeader(array('foo' => 'Bar', 'fOO-bAr' => 'quux')); - $this->assertEquals($r, array('Foo' => 'Bar', 'Foo-Bar' => 'quux')); + $this->assertEquals($r, array('foo' => 'Bar', 'fOO-bAr' => 'quux')); $r = $this->Socket->parseHeader(true); $this->assertEquals($r, false); @@ -1416,9 +1416,9 @@ class HttpSocketTest extends CakeTestCase { $header = "people: Jim,John\r\nfoo-LAND: Bar\r\ncAKe-PHP: rocks\r\n"; $r = $this->Socket->parseHeader($header); $expected = array( - 'People' => 'Jim,John' - , 'Foo-Land' => 'Bar' - , 'Cake-Php' => 'rocks' + 'people' => 'Jim,John' + , 'foo-LAND' => 'Bar' + , 'cAKe-PHP' => 'rocks' ); $this->assertEquals($r, $expected); From 974161cf3a75a791571b59dc506dc71035abd2ca Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 13 Nov 2010 21:47:25 -0200 Subject: [PATCH 13/52] Changed the name of header to use in Digest method. --- cake/libs/http/digest_method.php | 4 ++-- cake/tests/cases/libs/http/digest_method.test.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cake/libs/http/digest_method.php b/cake/libs/http/digest_method.php index 42d0c9482..654dc60fb 100644 --- a/cake/libs/http/digest_method.php +++ b/cake/libs/http/digest_method.php @@ -55,10 +55,10 @@ class DigestMethod { $http->request($http->request); $http->request = $originalRequest; - if (empty($http->response['header']['Www-Authenticate'])) { + if (empty($http->response['header']['WWW-Authenticate'])) { return false; } - preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $http->response['header']['Www-Authenticate'], $matches, PREG_SET_ORDER); + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $http->response['header']['WWW-Authenticate'], $matches, PREG_SET_ORDER); foreach ($matches as $match) { $http->config['request']['auth'][$match[1]] = $match[2]; } diff --git a/cake/tests/cases/libs/http/digest_method.test.php b/cake/tests/cases/libs/http/digest_method.test.php index c5a67542b..8b1715b7f 100644 --- a/cake/tests/cases/libs/http/digest_method.test.php +++ b/cake/tests/cases/libs/http/digest_method.test.php @@ -37,7 +37,7 @@ class DigestHttpSocket extends HttpSocket { * @return void */ public function request($request) { - $this->response['header']['Www-Authenticate'] = $this->nextHeader; + $this->response['header']['WWW-Authenticate'] = $this->nextHeader; } } From 7bea5d941070ec8f8767b7e9d6ef3fccb4dd89df Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 14 Nov 2010 23:56:12 -0200 Subject: [PATCH 14/52] Added the http directory to AllSocket tests. --- cake/tests/cases/libs/all_socket.test.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cake/tests/cases/libs/all_socket.test.php b/cake/tests/cases/libs/all_socket.test.php index 60802436e..896738957 100644 --- a/cake/tests/cases/libs/all_socket.test.php +++ b/cake/tests/cases/libs/all_socket.test.php @@ -34,10 +34,11 @@ class AllSocketTest extends PHPUnit_Framework_TestSuite { * @return void */ public static function suite() { - $suite = new PHPUnit_Framework_TestSuite('All Socket related class tests'); + $suite = new CakeTestSuite('All Socket related class tests'); $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'cake_socket.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'http_socket.test.php'); + $suite->addTestDirectory(CORE_TEST_CASES . DS . 'libs' . DS . 'http'); return $suite; } } \ No newline at end of file From 7e0d34903eede19eaff8efac542274d359b60e5f Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 15 Nov 2010 00:09:52 -0200 Subject: [PATCH 15/52] Removed use of reference in params of http methods. --- cake/libs/http/basic_method.php | 2 +- cake/libs/http/digest_method.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cake/libs/http/basic_method.php b/cake/libs/http/basic_method.php index da7951c6c..a0d1590c7 100644 --- a/cake/libs/http/basic_method.php +++ b/cake/libs/http/basic_method.php @@ -33,7 +33,7 @@ class BasicMethod { * @return void * @throws Exception */ - public static function authentication(&$http) { + public static function authentication(HttpSocket $http) { if (isset($http->request['auth']['user'], $http->request['auth']['pass'])) { $http->request['header']['Authorization'] = 'Basic ' . base64_encode($http->request['auth']['user'] . ':' . $http->request['auth']['pass']); } diff --git a/cake/libs/http/digest_method.php b/cake/libs/http/digest_method.php index 654dc60fb..29c068bca 100644 --- a/cake/libs/http/digest_method.php +++ b/cake/libs/http/digest_method.php @@ -34,7 +34,7 @@ class DigestMethod { * @throws Exception * @link http://www.ietf.org/rfc/rfc2617.txt */ - public static function authentication(&$http) { + public static function authentication(HttpSocket $http) { if (isset($http->request['auth']['user'], $http->request['auth']['pass'])) { if (!isset($http->config['request']['auth']['realm']) && !self::_getServerInformation($http)) { return; @@ -49,7 +49,7 @@ class DigestMethod { * @param HttpSocket $http * @return boolean */ - protected static function _getServerInformation(&$http) { + protected static function _getServerInformation(HttpSocket $http) { $originalRequest = $http->request; $http->request['auth'] = array('method' => false); $http->request($http->request); @@ -74,7 +74,7 @@ class DigestMethod { * @param HttpSocket $http * @return string */ - protected static function _generateHeader(&$http) { + protected static function _generateHeader(HttpSocket $http) { $a1 = md5($http->request['auth']['user'] . ':' . $http->config['request']['auth']['realm'] . ':' . $http->request['auth']['pass']); $a2 = md5($http->request['method'] . ':' . $http->request['uri']['path']); From 3056fbf680572fc63b2e53f7de5355fc60051c86 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 20 Nov 2010 17:47:35 -0200 Subject: [PATCH 16/52] Changing authentication classes to use suffix Authentication instead of Method. --- ...ic_method.php => basic_authentication.php} | 2 +- ...t_method.php => digest_authentication.php} | 2 +- cake/libs/http_socket.php | 2 +- ...test.php => basic_authentication.test.php} | 4 +-- ...est.php => digest_authentication.test.php} | 26 +++++++++---------- 5 files changed, 18 insertions(+), 18 deletions(-) rename cake/libs/http/{basic_method.php => basic_authentication.php} (97%) rename cake/libs/http/{digest_method.php => digest_authentication.php} (99%) rename cake/tests/cases/libs/http/{basic_method.test.php => basic_authentication.test.php} (92%) rename cake/tests/cases/libs/http/{digest_method.test.php => digest_authentication.test.php} (89%) diff --git a/cake/libs/http/basic_method.php b/cake/libs/http/basic_authentication.php similarity index 97% rename from cake/libs/http/basic_method.php rename to cake/libs/http/basic_authentication.php index a0d1590c7..7cd01b7cb 100644 --- a/cake/libs/http/basic_method.php +++ b/cake/libs/http/basic_authentication.php @@ -24,7 +24,7 @@ * @package cake * @subpackage cake.cake.libs.http */ -class BasicMethod { +class BasicAuthentication { /** * Authentication diff --git a/cake/libs/http/digest_method.php b/cake/libs/http/digest_authentication.php similarity index 99% rename from cake/libs/http/digest_method.php rename to cake/libs/http/digest_authentication.php index 29c068bca..b25c6c971 100644 --- a/cake/libs/http/digest_method.php +++ b/cake/libs/http/digest_authentication.php @@ -24,7 +24,7 @@ * @package cake * @subpackage cake.cake.libs.http */ -class DigestMethod { +class DigestAuthentication { /** * Authentication diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 0df3a63e1..da67a28ab 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -455,7 +455,7 @@ class HttpSocket extends CakeSocket { return; } } - $authClass = Inflector::camelize($this->request['auth']['method']) . 'Method'; + $authClass = Inflector::camelize($this->request['auth']['method']) . 'Authentication'; if (!App::import('Lib', 'http/' . $authClass)) { throw new Exception(__('Unknown authentication method.')); } diff --git a/cake/tests/cases/libs/http/basic_method.test.php b/cake/tests/cases/libs/http/basic_authentication.test.php similarity index 92% rename from cake/tests/cases/libs/http/basic_method.test.php rename to cake/tests/cases/libs/http/basic_authentication.test.php index c9d487f70..9843e4815 100644 --- a/cake/tests/cases/libs/http/basic_method.test.php +++ b/cake/tests/cases/libs/http/basic_authentication.test.php @@ -19,7 +19,7 @@ */ App::import('Core', 'HttpSocket'); -App::import('Lib', 'http/BasicMethod'); +App::import('Lib', 'http/BasicAuthentication'); /** * BasicMethodTest class @@ -42,7 +42,7 @@ class BasicMethodTest extends CakeTestCase { 'pass' => 'secret' ); - BasicMethod::authentication($http); + BasicAuthentication::authentication($http); $this->assertEqual($http->request['header']['Authorization'], 'Basic bWFyazpzZWNyZXQ='); } diff --git a/cake/tests/cases/libs/http/digest_method.test.php b/cake/tests/cases/libs/http/digest_authentication.test.php similarity index 89% rename from cake/tests/cases/libs/http/digest_method.test.php rename to cake/tests/cases/libs/http/digest_authentication.test.php index 8b1715b7f..7862f3147 100644 --- a/cake/tests/cases/libs/http/digest_method.test.php +++ b/cake/tests/cases/libs/http/digest_authentication.test.php @@ -1,6 +1,6 @@ HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; $this->HttpSocket->config['request']['auth'] = array(); $this->assertFalse(isset($this->HttpSocket->request['header']['Authorization'])); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $this->assertTrue(isset($this->HttpSocket->request['header']['Authorization'])); $this->assertEqual($this->HttpSocket->config['request']['auth']['realm'], 'The batcave'); $this->assertEqual($this->HttpSocket->config['request']['auth']['nonce'], '4cded326c6c51'); @@ -105,7 +105,7 @@ class DigestMethodTest extends CakeTestCase { public function testQop() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; $this->HttpSocket->config['request']['auth'] = array(); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $expected = 'Digest username="admin", realm="The batcave", nonce="4cded326c6c51", uri="/", response="da7e2a46b471d77f70a9bb3698c8902b"'; $this->assertEqual($expected, $this->HttpSocket->request['header']['Authorization']); $this->assertFalse(isset($this->HttpSocket->config['request']['auth']['qop'])); @@ -113,7 +113,7 @@ class DigestMethodTest extends CakeTestCase { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",qop="auth"'; $this->HttpSocket->config['request']['auth'] = array(); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $expected = '@Digest username="admin", realm="The batcave", nonce="4cded326c6c51", uri="/", response="[a-z0-9]{32}", qop="auth", nc=00000001, cnonce="[a-z0-9]+"@'; $this->assertPattern($expected, $this->HttpSocket->request['header']['Authorization']); $this->assertEqual($this->HttpSocket->config['request']['auth']['qop'], 'auth'); @@ -128,12 +128,12 @@ class DigestMethodTest extends CakeTestCase { public function testOpaque() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; $this->HttpSocket->config['request']['auth'] = array(); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $this->assertFalse(strpos($this->HttpSocket->request['header']['Authorization'], 'opaque="d8ea7aa61a1693024c4cc3a516f49b3c"')); $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",opaque="d8ea7aa61a1693024c4cc3a516f49b3c"'; $this->HttpSocket->config['request']['auth'] = array(); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'opaque="d8ea7aa61a1693024c4cc3a516f49b3c"') > 0); } @@ -145,18 +145,18 @@ class DigestMethodTest extends CakeTestCase { public function testMultipleRequest() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",qop="auth"'; $this->HttpSocket->config['request']['auth'] = array(); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000001') > 0); $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 2); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000002') > 0); $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 3); $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); $response = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); $this->HttpSocket->nextHeader = ''; - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000003') > 0); $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 4); $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); @@ -173,7 +173,7 @@ class DigestMethodTest extends CakeTestCase { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; $this->HttpSocket->request['uri']['path'] = '/admin'; $this->HttpSocket->config['request']['auth'] = array(); - DigestMethod::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket); $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); $response = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); $this->assertNotEqual($response, 'da7e2a46b471d77f70a9bb3698c8902b'); From af06b8f1791e658a0299095a6cea062afd0cc847 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 24 Nov 2010 21:14:52 -0200 Subject: [PATCH 17/52] Added test to a request that server dont response WWW-Authenticate. --- .../libs/http/digest_authentication.test.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cake/tests/cases/libs/http/digest_authentication.test.php b/cake/tests/cases/libs/http/digest_authentication.test.php index 7862f3147..75e98595e 100644 --- a/cake/tests/cases/libs/http/digest_authentication.test.php +++ b/cake/tests/cases/libs/http/digest_authentication.test.php @@ -37,6 +37,12 @@ class DigestHttpSocket extends HttpSocket { * @return void */ public function request($request) { + if ($request === false) { + if (isset($this->response['header']['WWW-Authenticate'])) { + unset($this->response['header']['WWW-Authenticate']); + } + return; + } $this->response['header']['WWW-Authenticate'] = $this->nextHeader; } @@ -179,4 +185,17 @@ class DigestAuthenticationTest extends CakeTestCase { $this->assertNotEqual($response, 'da7e2a46b471d77f70a9bb3698c8902b'); } +/** + * testNoDigestResponse method + * + * @return void + */ + public function testNoDigestResponse() { + $this->HttpSocket->nextHeader = false; + $this->HttpSocket->request['uri']['path'] = '/admin'; + $this->HttpSocket->config['request']['auth'] = array(); + DigestAuthentication::authentication($this->HttpSocket); + $this->assertFalse(isset($this->HttpSocket->request['header']['Authorization'])); + } + } \ No newline at end of file From 547f48fb03ca6a0247028fcf8453e6431b4f25a2 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 26 Nov 2010 23:00:26 -0200 Subject: [PATCH 18/52] Changing to be compatible with PHP 5.2+ --- cake/libs/http_socket.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index da67a28ab..8ae54948c 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -459,7 +459,7 @@ class HttpSocket extends CakeSocket { if (!App::import('Lib', 'http/' . $authClass)) { throw new Exception(__('Unknown authentication method.')); } - $authClass::authentication($this); + call_user_func("$authClass::authentication", $this); } /** From b9010b05f2f272c3ba2c7632223f6f1f2837090a Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 1 Dec 2010 13:46:13 -0200 Subject: [PATCH 19/52] Support to proxy in HttpSocket. --- cake/libs/http_socket.php | 43 +++++++++- cake/tests/cases/libs/http_socket.test.php | 99 ++++++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 1297c122f..ad943bbcb 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -64,6 +64,13 @@ class HttpSocket extends CakeSocket { 'user' => null, 'pass' => null ), + 'proxy' => array( + 'method' => 'Basic', + 'host' => null, + 'port' => 3128, + 'user' => null, + 'pass' => null + ), 'version' => '1.1', 'body' => '', 'line' => null, @@ -121,6 +128,13 @@ class HttpSocket extends CakeSocket { 'user' => null, 'pass' => null ), + 'proxy' => array( + 'method' => 'Basic', + 'host' => null, + 'port' => 3128, + 'user' => null, + 'pass' => null + ), 'cookies' => array() ) ); @@ -238,6 +252,7 @@ class HttpSocket extends CakeSocket { } $this->_setAuth(); + $this->_setProxyConfig(); if (is_array($this->request['body'])) { $this->request['body'] = $this->_httpSerialize($this->request['body']); @@ -463,6 +478,28 @@ class HttpSocket extends CakeSocket { call_user_func("$authClass::authentication", $this); } +/** + * Set the proxy configuration and authentication + * + * @return void + */ + protected function _setProxyConfig() { + if (empty($this->request['proxy']['host'])) { + return; + } + $this->config['host'] = $this->request['proxy']['host']; + $this->config['port'] = $this->request['proxy']['port']; + + if (empty($this->request['proxy']['method']) || !isset($this->request['proxy']['user'], $this->request['proxy']['pass'])) { + return; + } + $authClass = Inflector::camelize($this->request['proxy']['method']) . 'Authentication'; + if (!App::import('Lib', 'http/' . $authClass)) { + throw new Exception(__('Unknown authentication method for proxy.')); + } + call_user_func("$authClass::proxyAuthentication", $this); + } + /** * Parses the given message and breaks it down in parts. * @@ -837,7 +874,11 @@ class HttpSocket extends CakeSocket { $request['uri'] = $this->_parseUri($request['uri']); $request = array_merge(array('method' => 'GET'), $request); - $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query'); + if (!empty($request['proxy']['host'])) { + $request['uri'] = $this->_buildUri($request['uri'], '%scheme://%host:%port/%path?%query'); + } else { + $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query'); + } if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { throw new Exception(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.'), join(',', $asteriskMethods))); diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index fe6ec1c70..877d52ac1 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -19,6 +19,36 @@ */ App::import('Core', 'HttpSocket'); +/** + * TestAuthentication class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class TestAuthentication { + +/** + * authentication method + * + * @param HttpSocket $http + * @return void + */ + public static function authentication(HttpSocket $http) { + $http->request['header']['Authorization'] = 'Test ' . $http->request['auth']['user'] . '.' . $http->request['auth']['pass']; + } + +/** + * proxyAuthentication method + * + * @param HttpSocket $http + * @return void + */ + public static function proxyAuthentication(HttpSocket $http) { + $http->request['header']['Proxy-Authorization'] = 'Test ' . $http->request['proxy']['user'] . '.' . $http->request['proxy']['pass']; + } + +} + class TestHttpSocket extends HttpSocket { /** @@ -261,6 +291,13 @@ class HttpSocketTest extends CakeTestCase { , 'user' => 'bob' , 'pass' => 'secret' ), + 'proxy' => array( + 'method' => 'Basic', + 'host' => null, + 'port' => 3128, + 'user' => null, + 'pass' => null + ), 'cookies' => array(), ) ); @@ -290,6 +327,13 @@ class HttpSocketTest extends CakeTestCase { , 'user' => null , 'pass' => null ), + 'proxy' => array( + 'method' => 'Basic', + 'host' => null, + 'port' => 3128, + 'user' => null, + 'pass' => null + ), 'cookies' => array() ) ); @@ -336,6 +380,13 @@ class HttpSocketTest extends CakeTestCase { ,'user' => null ,'pass' => null ), + 'proxy' => array( + 'method' => 'Basic', + 'host' => null, + 'port' => 3128, + 'user' => null, + 'pass' => null + ), 'cookies' => array(), ), ) @@ -355,6 +406,13 @@ class HttpSocketTest extends CakeTestCase { 'method' => 'Basic' , 'user' => null , 'pass' => null + ), + 'proxy' => array( + 'method' => 'Basic', + 'host' => null, + 'port' => 3128, + 'user' => null, + 'pass' => null ) , 'version' => '1.1' , 'body' => '' @@ -588,6 +646,47 @@ class HttpSocketTest extends CakeTestCase { $this->assertFalse($this->Socket->connected); } +/** + * testProxy method + * + * @return void + */ + function testProxy() { + $this->Socket->reset(); + $this->Socket->expects($this->any())->method('connect')->will($this->returnValue(true)); + $this->Socket->expects($this->any())->method('read')->will($this->returnValue(false)); + $request = array( + 'uri' => 'http://www.cakephp.org/', + 'proxy' => array( + 'host' => 'proxy.server', + 'port' => 123 + ) + ); + $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n"; + $this->Socket->request($request); + $this->assertEqual($this->Socket->request['raw'], $expected); + $this->assertEqual($this->Socket->config['host'], 'proxy.server'); + $this->assertEqual($this->Socket->config['port'], 123); + + $request['proxy']['method'] = 'Test'; + $request['proxy']['user'] = 'mark'; + $request['proxy']['pass'] = 'secret'; + $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; + $this->Socket->request($request); + $this->assertEqual($this->Socket->request['raw'], $expected); + $this->assertEqual($this->Socket->config['host'], 'proxy.server'); + $this->assertEqual($this->Socket->config['port'], 123); + + $request['auth'] = array( + 'method' => 'Test', + 'user' => 'login', + 'pass' => 'passwd' + ); + $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nAuthorization: Test login.passwd\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; + $this->Socket->request($request); + $this->assertEqual($this->Socket->request['raw'], $expected); + } + /** * testUrl method * From 64dcca61ef2479e1d92226feba6d12c23291ff9d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 1 Dec 2010 13:49:03 -0200 Subject: [PATCH 20/52] Throw exception when authentication class dont support authentication/proxyAuthentication method. --- cake/libs/http_socket.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index ad943bbcb..70a86c582 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -475,6 +475,9 @@ class HttpSocket extends CakeSocket { if (!App::import('Lib', 'http/' . $authClass)) { throw new Exception(__('Unknown authentication method.')); } + if (!method_exists($authClass, 'authentication')) { + throw new Exception(sprintf(__('The %s do not support authentication.'), $authClass)); + } call_user_func("$authClass::authentication", $this); } @@ -497,6 +500,9 @@ class HttpSocket extends CakeSocket { if (!App::import('Lib', 'http/' . $authClass)) { throw new Exception(__('Unknown authentication method for proxy.')); } + if (!method_exists($authClass, 'proxyAuthentication')) { + throw new Exception(sprintf(__('The %s do not support proxy authentication.'), $authClass)); + } call_user_func("$authClass::proxyAuthentication", $this); } From e1e802639252a231d9c8faf3aa37c91043492d9c Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 1 Dec 2010 14:00:10 -0200 Subject: [PATCH 21/52] Support to proxy authentication in basic authentication. --- cake/libs/http/basic_authentication.php | 28 +++++++++++++++++-- .../libs/http/basic_authentication.test.php | 17 +++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cake/libs/http/basic_authentication.php b/cake/libs/http/basic_authentication.php index 7cd01b7cb..4a857e68e 100644 --- a/cake/libs/http/basic_authentication.php +++ b/cake/libs/http/basic_authentication.php @@ -31,12 +31,36 @@ class BasicAuthentication { * * @param HttpSocket $http * @return void - * @throws Exception + * @see http://www.ietf.org/rfc/rfc2617.txt */ public static function authentication(HttpSocket $http) { if (isset($http->request['auth']['user'], $http->request['auth']['pass'])) { - $http->request['header']['Authorization'] = 'Basic ' . base64_encode($http->request['auth']['user'] . ':' . $http->request['auth']['pass']); + $http->request['header']['Authorization'] = self::_generateHeader($http->request['auth']['user'], $http->request['auth']['pass']); } } +/** + * Proxy Authentication + * + * @param HttpSocket $http + * @return void + * @see http://www.ietf.org/rfc/rfc2617.txt + */ + public static function proxyAuthentication(HttpSocket $http) { + if (isset($http->request['proxy']['user'], $http->request['proxy']['pass'])) { + $http->request['header']['Proxy-Authorization'] = self::_generateHeader($http->request['proxy']['user'], $http->request['proxy']['pass']); + } + } + +/** + * Generate basic [proxy] authentication header + * + * @param string $user + * @param string $pass + * @return string + */ + protected static function _generateHeader($user, $pass) { + return 'Basic ' . base64_encode($user . ':' . $pass); + } + } diff --git a/cake/tests/cases/libs/http/basic_authentication.test.php b/cake/tests/cases/libs/http/basic_authentication.test.php index 9843e4815..3df447b55 100644 --- a/cake/tests/cases/libs/http/basic_authentication.test.php +++ b/cake/tests/cases/libs/http/basic_authentication.test.php @@ -46,4 +46,21 @@ class BasicMethodTest extends CakeTestCase { $this->assertEqual($http->request['header']['Authorization'], 'Basic bWFyazpzZWNyZXQ='); } +/** + * testProxyAuthentication method + * + * @return void + */ + public function testProxyAuthentication() { + $http = new HttpSocket(); + $http->request['proxy'] = array( + 'method' => 'Basic', + 'user' => 'mark', + 'pass' => 'secret' + ); + + BasicAuthentication::proxyAuthentication($http); + $this->assertEqual($http->request['header']['Proxy-Authorization'], 'Basic bWFyazpzZWNyZXQ='); + } + } \ No newline at end of file From cd24aca39d9262fa43410566b6ddbe29eae7c9ef Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 3 Dec 2010 00:46:11 -0200 Subject: [PATCH 22/52] Removing auth key from request and putting this as attribute. --- cake/libs/http/basic_authentication.php | 7 +- cake/libs/http_socket.php | 70 ++++++++++--------- .../libs/http/basic_authentication.test.php | 4 +- cake/tests/cases/libs/http_socket.test.php | 40 ++--------- 4 files changed, 46 insertions(+), 75 deletions(-) diff --git a/cake/libs/http/basic_authentication.php b/cake/libs/http/basic_authentication.php index 4a857e68e..393f4a867 100644 --- a/cake/libs/http/basic_authentication.php +++ b/cake/libs/http/basic_authentication.php @@ -30,12 +30,13 @@ class BasicAuthentication { * Authentication * * @param HttpSocket $http + * @param array $authInfo * @return void * @see http://www.ietf.org/rfc/rfc2617.txt */ - public static function authentication(HttpSocket $http) { - if (isset($http->request['auth']['user'], $http->request['auth']['pass'])) { - $http->request['header']['Authorization'] = self::_generateHeader($http->request['auth']['user'], $http->request['auth']['pass']); + public static function authentication(HttpSocket $http, &$authInfo) { + if (isset($authInfo['user'], $authInfo['pass'])) { + $http->request['header']['Authorization'] = self::_generateHeader($authInfo['user'], $authInfo['pass']); } } diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 70a86c582..45abaa079 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -59,11 +59,6 @@ class HttpSocket extends CakeSocket { 'query' => null, 'fragment' => null ), - 'auth' => array( - 'method' => 'Basic', - 'user' => null, - 'pass' => null - ), 'proxy' => array( 'method' => 'Basic', 'host' => null, @@ -123,11 +118,6 @@ class HttpSocket extends CakeSocket { 'host' => 'localhost', 'port' => 80 ), - 'auth' => array( - 'method' => 'Basic', - 'user' => null, - 'pass' => null - ), 'proxy' => array( 'method' => 'Basic', 'host' => null, @@ -147,6 +137,14 @@ class HttpSocket extends CakeSocket { */ public $lineBreak = "\r\n"; +/** + * Authentication settings + * + * @var array + * @access protected + */ + protected $_auth = array(); + /** * Build an HTTP Socket using the specified configuration. * @@ -181,6 +179,26 @@ class HttpSocket extends CakeSocket { parent::__construct($this->config); } +/** + * Set authentication settings + * + * @param string $method Authentication method (ex. Basic, Digest). If empty, disable authentication + * @param mixed $user Username for authentication. Can be an array with settings to authentication class + * @param string $pass Password for authentication + * @return void + */ + public function setAuthConfig($method, $user, $pass = null) { + if (empty($method)) { + $this->_auth = array(); + return; + } + if (is_array($user)) { + $this->_auth = array($method => $user); + return; + } + $this->_auth = array($method => compact('user', 'pass')); + } + /** * Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this * method and provide a more granular interface. @@ -201,10 +219,6 @@ class HttpSocket extends CakeSocket { $request['uri'] = null; } $uri = $this->_parseUri($request['uri']); - $hadAuth = false; - if (is_array($uri) && array_key_exists('user', $uri)) { - $hadAuth = true; - } if (!isset($uri['host'])) { $host = $this->config['host']; } @@ -216,10 +230,6 @@ class HttpSocket extends CakeSocket { $request['uri'] = $this->_parseUri($request['uri'], true); $this->request = Set::merge($this->request, $this->config['request'], $request); - if (!$hadAuth && !empty($this->config['request']['auth']['user'])) { - $this->request['uri']['user'] = $this->config['request']['auth']['user']; - $this->request['uri']['pass'] = $this->config['request']['auth']['pass']; - } $this->_configUri($this->request['uri']); if (isset($host)) { @@ -251,6 +261,9 @@ class HttpSocket extends CakeSocket { $this->request['header'] = array_merge(compact('Host'), $this->request['header']); } + if (isset($this->request['uri']['user'], $this->request['uri']['pass'])) { + $this->setAuthConfig('Basic', $this->request['uri']['user'], $this->request['uri']['pass']); + } $this->_setAuth(); $this->_setProxyConfig(); @@ -457,28 +470,18 @@ class HttpSocket extends CakeSocket { * @throws Exception */ protected function _setAuth() { - if ($this->request['auth']['method'] === false) { + if (empty($this->_auth)) { return; } - if (empty($this->request['auth']['method'])) { - if (isset($this->request['uri']['user'], $this->request['uri']['pass']) && !isset($this->request['auth']['user'])) { - $this->request['auth'] = array( - 'method' => 'Basic', - 'user' => $this->request['uri']['user'], - 'pass' => $this->request['uri']['pass'] - ); - } else { - return; - } - } - $authClass = Inflector::camelize($this->request['auth']['method']) . 'Authentication'; + $method = key($this->_auth); + $authClass = Inflector::camelize($method) . 'Authentication'; if (!App::import('Lib', 'http/' . $authClass)) { throw new Exception(__('Unknown authentication method.')); } if (!method_exists($authClass, 'authentication')) { throw new Exception(sprintf(__('The %s do not support authentication.'), $authClass)); } - call_user_func("$authClass::authentication", $this); + call_user_func("$authClass::authentication", $this, &$this->_auth[$method]); } /** @@ -677,8 +680,7 @@ class HttpSocket extends CakeSocket { } $config = array( 'request' => array( - 'uri' => array_intersect_key($uri, $this->config['request']['uri']), - 'auth' => array_intersect_key($uri, $this->config['request']['auth']) + 'uri' => array_intersect_key($uri, $this->config['request']['uri']) ) ); $this->config = Set::merge($this->config, $config); diff --git a/cake/tests/cases/libs/http/basic_authentication.test.php b/cake/tests/cases/libs/http/basic_authentication.test.php index 3df447b55..7ba642b64 100644 --- a/cake/tests/cases/libs/http/basic_authentication.test.php +++ b/cake/tests/cases/libs/http/basic_authentication.test.php @@ -36,13 +36,13 @@ class BasicMethodTest extends CakeTestCase { */ public function testAuthentication() { $http = new HttpSocket(); - $http->request['auth'] = array( + $auth = array( 'method' => 'Basic', 'user' => 'mark', 'pass' => 'secret' ); - BasicAuthentication::authentication($http); + BasicAuthentication::authentication($http, $auth); $this->assertEqual($http->request['header']['Authorization'], 'Basic bWFyazpzZWNyZXQ='); } diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 877d52ac1..99f3f0cdb 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -31,10 +31,11 @@ class TestAuthentication { * authentication method * * @param HttpSocket $http + * @param array $authInfo * @return void */ - public static function authentication(HttpSocket $http) { - $http->request['header']['Authorization'] = 'Test ' . $http->request['auth']['user'] . '.' . $http->request['auth']['pass']; + public static function authentication(HttpSocket $http, &$authInfo) { + $http->request['header']['Authorization'] = 'Test ' . $authInfo['user'] . '.' . $authInfo['pass']; } /** @@ -286,11 +287,6 @@ class HttpSocketTest extends CakeTestCase { , 'host' => 'www.cakephp.org' , 'port' => 23 ), - 'auth' => array( - 'method' => 'Basic' - , 'user' => 'bob' - , 'pass' => 'secret' - ), 'proxy' => array( 'method' => 'Basic', 'host' => null, @@ -322,11 +318,6 @@ class HttpSocketTest extends CakeTestCase { , 'host' => 'www.foo.com' , 'port' => 80 ), - 'auth' => array( - 'method' => 'Basic' - , 'user' => null - , 'pass' => null - ), 'proxy' => array( 'method' => 'Basic', 'host' => null, @@ -374,11 +365,6 @@ class HttpSocketTest extends CakeTestCase { 'scheme' => 'http' , 'host' => 'www.cakephp.org' , 'port' => 80, - ) - , 'auth' => array( - 'method' => 'Basic' - ,'user' => null - ,'pass' => null ), 'proxy' => array( 'method' => 'Basic', @@ -401,11 +387,6 @@ class HttpSocketTest extends CakeTestCase { , 'path' => '/' , 'query' => array('foo' => 'bar') , 'fragment' => null - ) - , 'auth' => array( - 'method' => 'Basic' - , 'user' => null - , 'pass' => null ), 'proxy' => array( 'method' => 'Basic', @@ -677,11 +658,7 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($this->Socket->config['host'], 'proxy.server'); $this->assertEqual($this->Socket->config['port'], 123); - $request['auth'] = array( - 'method' => 'Test', - 'user' => 'login', - 'pass' => 'passwd' - ); + $this->Socket->setAuthConfig('Test', 'login', 'passwd'); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nAuthorization: Test login.passwd\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; $this->Socket->request($request); $this->assertEqual($this->Socket->request['raw'], $expected); @@ -785,24 +762,15 @@ class HttpSocketTest extends CakeTestCase { */ function testConsecutiveGetResetsAuthCredentials() { $socket = new MockHttpSocket(); - $socket->config['request']['auth'] = array( - 'method' => 'Basic', - 'user' => 'mark', - 'pass' => 'secret' - ); $socket->get('http://mark:secret@example.com/test'); $this->assertEqual($socket->request['uri']['user'], 'mark'); $this->assertEqual($socket->request['uri']['pass'], 'secret'); $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); $socket->get('/test2'); - $this->assertEqual($socket->request['auth']['user'], 'mark'); - $this->assertEqual($socket->request['auth']['pass'], 'secret'); $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); $socket->get('/test3'); - $this->assertEqual($socket->request['auth']['user'], 'mark'); - $this->assertEqual($socket->request['auth']['pass'], 'secret'); $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); } From 4325e671630a30bb8313d2364c990718a0e2ffcc Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 3 Dec 2010 01:30:03 -0200 Subject: [PATCH 23/52] Fixing setAuthConfig to accept false as param to remove auth config. Tests added. --- cake/libs/http_socket.php | 2 +- cake/tests/cases/libs/http_socket.test.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 45abaa079..d5ed518ad 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -187,7 +187,7 @@ class HttpSocket extends CakeSocket { * @param string $pass Password for authentication * @return void */ - public function setAuthConfig($method, $user, $pass = null) { + public function setAuthConfig($method, $user = null, $pass = null) { if (empty($method)) { $this->_auth = array(); return; diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 99f3f0cdb..06b055928 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -755,6 +755,25 @@ class HttpSocketTest extends CakeTestCase { $this->RequestSocket->get('http://www.google.com/', null, array('auth' => array('user' => 'foo', 'pass' => 'bar'))); } +/** + * Test authentication + * + * @return void + */ + public function testAuth() { + $socket = new MockHttpSocket(); + $socket->get('http://mark:secret@example.com/test'); + $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); + + $socket->setAuthConfig(false); + $socket->get('http://example.com/test'); + $this->assertFalse(strpos($socket->request['header'], 'Authorization:')); + + $socket->setAuthConfig('Test', 'mark', 'passwd'); + $socket->get('http://example.com/test'); + $this->assertTrue(strpos($socket->request['header'], 'Authorization: Test mark.passwd') !== false); + } + /** * test that two consecutive get() calls reset the authentication credentials. * From f004bef74fac8db830fe8e7e8f86a303b4dafdaf Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 3 Dec 2010 21:57:15 -0200 Subject: [PATCH 24/52] DigestAuthentication adapted to new auth in HttpSocket. --- cake/libs/http/digest_authentication.php | 50 +++++++++------- .../libs/http/digest_authentication.test.php | 60 +++++++++---------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/cake/libs/http/digest_authentication.php b/cake/libs/http/digest_authentication.php index b25c6c971..ba146e2fc 100644 --- a/cake/libs/http/digest_authentication.php +++ b/cake/libs/http/digest_authentication.php @@ -30,16 +30,17 @@ class DigestAuthentication { * Authentication * * @param HttpSocket $http + * @param array $authInfo * @return void * @throws Exception * @link http://www.ietf.org/rfc/rfc2617.txt */ - public static function authentication(HttpSocket $http) { - if (isset($http->request['auth']['user'], $http->request['auth']['pass'])) { - if (!isset($http->config['request']['auth']['realm']) && !self::_getServerInformation($http)) { + public static function authentication(HttpSocket $http, &$authInfo) { + if (isset($authInfo['user'], $authInfo['pass'])) { + if (!isset($authInfo['realm']) && !self::_getServerInformation($http, $authInfo)) { return; } - $http->request['header']['Authorization'] = self::_generateHeader($http); + $http->request['header']['Authorization'] = self::_generateHeader($http, $authInfo); } } @@ -47,23 +48,25 @@ class DigestAuthentication { * Retrive information about the authetication * * @param HttpSocket $http + * @parma array $authInfo * @return boolean */ - protected static function _getServerInformation(HttpSocket $http) { + protected static function _getServerInformation(HttpSocket $http, &$authInfo) { $originalRequest = $http->request; - $http->request['auth'] = array('method' => false); + $http->setAuthConfig(false); $http->request($http->request); $http->request = $originalRequest; + $http->setAuthConfig('Digest', $authInfo); if (empty($http->response['header']['WWW-Authenticate'])) { return false; } preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $http->response['header']['WWW-Authenticate'], $matches, PREG_SET_ORDER); foreach ($matches as $match) { - $http->config['request']['auth'][$match[1]] = $match[2]; + $authInfo[$match[1]] = $match[2]; } - if (!empty($http->config['request']['auth']['qop']) && empty($http->config['request']['auth']['nc'])) { - $http->config['request']['auth']['nc'] = 1; + if (!empty($authInfo['qop']) && empty($authInfo['nc'])) { + $authInfo['nc'] = 1; } return true; } @@ -72,31 +75,32 @@ class DigestAuthentication { * Generate the header Authorization * * @param HttpSocket $http + * @param array $authInfo * @return string */ - protected static function _generateHeader(HttpSocket $http) { - $a1 = md5($http->request['auth']['user'] . ':' . $http->config['request']['auth']['realm'] . ':' . $http->request['auth']['pass']); + protected static function _generateHeader(HttpSocket $http, &$authInfo) { + $a1 = md5($authInfo['user'] . ':' . $authInfo['realm'] . ':' . $authInfo['pass']); $a2 = md5($http->request['method'] . ':' . $http->request['uri']['path']); - if (empty($http->config['request']['auth']['qop'])) { - $response = md5($a1 . ':' . $http->config['request']['auth']['nonce'] . ':' . $a2); + if (empty($authInfo['qop'])) { + $response = md5($a1 . ':' . $authInfo['nonce'] . ':' . $a2); } else { - $http->config['request']['auth']['cnonce'] = uniqid(); - $nc = sprintf('%08x', $http->config['request']['auth']['nc']++); - $response = md5($a1 . ':' . $http->config['request']['auth']['nonce'] . ':' . $nc . ':' . $http->config['request']['auth']['cnonce'] . ':auth:' . $a2); + $authInfo['cnonce'] = uniqid(); + $nc = sprintf('%08x', $authInfo['nc']++); + $response = md5($a1 . ':' . $authInfo['nonce'] . ':' . $nc . ':' . $authInfo['cnonce'] . ':auth:' . $a2); } $authHeader = 'Digest '; - $authHeader .= 'username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $http->request['auth']['user']) . '", '; - $authHeader .= 'realm="' . $http->config['request']['auth']['realm'] . '", '; - $authHeader .= 'nonce="' . $http->config['request']['auth']['nonce'] . '", '; + $authHeader .= 'username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $authInfo['user']) . '", '; + $authHeader .= 'realm="' . $authInfo['realm'] . '", '; + $authHeader .= 'nonce="' . $authInfo['nonce'] . '", '; $authHeader .= 'uri="' . $http->request['uri']['path'] . '", '; $authHeader .= 'response="' . $response . '"'; - if (!empty($http->config['request']['auth']['opaque'])) { - $authHeader .= ', opaque="' . $http->config['request']['auth']['opaque'] . '"'; + if (!empty($authInfo['opaque'])) { + $authHeader .= ', opaque="' . $authInfo['opaque'] . '"'; } - if (!empty($http->config['request']['auth']['qop'])) { - $authHeader .= ', qop="auth", nc=' . $nc . ', cnonce="' . $http->config['request']['auth']['cnonce'] . '"'; + if (!empty($authInfo['qop'])) { + $authHeader .= ', qop="auth", nc=' . $nc . ', cnonce="' . $authInfo['cnonce'] . '"'; } return $authHeader; } diff --git a/cake/tests/cases/libs/http/digest_authentication.test.php b/cake/tests/cases/libs/http/digest_authentication.test.php index 75e98595e..2166aee24 100644 --- a/cake/tests/cases/libs/http/digest_authentication.test.php +++ b/cake/tests/cases/libs/http/digest_authentication.test.php @@ -72,11 +72,6 @@ class DigestAuthenticationTest extends CakeTestCase { $this->HttpSocket = new DigestHttpSocket(); $this->HttpSocket->request['method'] = 'GET'; $this->HttpSocket->request['uri']['path'] = '/'; - $this->HttpSocket->request['auth'] = array( - 'method' => 'Digest', - 'user' => 'admin', - 'pass' => '1234' - ); } /** @@ -95,12 +90,13 @@ class DigestAuthenticationTest extends CakeTestCase { */ public function testBasic() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; - $this->HttpSocket->config['request']['auth'] = array(); $this->assertFalse(isset($this->HttpSocket->request['header']['Authorization'])); - DigestAuthentication::authentication($this->HttpSocket); + + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertTrue(isset($this->HttpSocket->request['header']['Authorization'])); - $this->assertEqual($this->HttpSocket->config['request']['auth']['realm'], 'The batcave'); - $this->assertEqual($this->HttpSocket->config['request']['auth']['nonce'], '4cded326c6c51'); + $this->assertEqual($auth['realm'], 'The batcave'); + $this->assertEqual($auth['nonce'], '4cded326c6c51'); } /** @@ -110,20 +106,20 @@ class DigestAuthenticationTest extends CakeTestCase { */ public function testQop() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $expected = 'Digest username="admin", realm="The batcave", nonce="4cded326c6c51", uri="/", response="da7e2a46b471d77f70a9bb3698c8902b"'; $this->assertEqual($expected, $this->HttpSocket->request['header']['Authorization']); - $this->assertFalse(isset($this->HttpSocket->config['request']['auth']['qop'])); - $this->assertFalse(isset($this->HttpSocket->config['request']['auth']['nc'])); + $this->assertFalse(isset($auth['qop'])); + $this->assertFalse(isset($auth['nc'])); $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",qop="auth"'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $expected = '@Digest username="admin", realm="The batcave", nonce="4cded326c6c51", uri="/", response="[a-z0-9]{32}", qop="auth", nc=00000001, cnonce="[a-z0-9]+"@'; $this->assertPattern($expected, $this->HttpSocket->request['header']['Authorization']); - $this->assertEqual($this->HttpSocket->config['request']['auth']['qop'], 'auth'); - $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 2); + $this->assertEqual($auth['qop'], 'auth'); + $this->assertEqual($auth['nc'], 2); } /** @@ -133,13 +129,13 @@ class DigestAuthenticationTest extends CakeTestCase { */ public function testOpaque() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertFalse(strpos($this->HttpSocket->request['header']['Authorization'], 'opaque="d8ea7aa61a1693024c4cc3a516f49b3c"')); $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",opaque="d8ea7aa61a1693024c4cc3a516f49b3c"'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'opaque="d8ea7aa61a1693024c4cc3a516f49b3c"') > 0); } @@ -150,21 +146,21 @@ class DigestAuthenticationTest extends CakeTestCase { */ public function testMultipleRequest() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51",qop="auth"'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000001') > 0); - $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 2); + $this->assertEqual($auth['nc'], 2); - DigestAuthentication::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000002') > 0); - $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 3); + $this->assertEqual($auth['nc'], 3); $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); $response = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); $this->HttpSocket->nextHeader = ''; - DigestAuthentication::authentication($this->HttpSocket); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertTrue(strpos($this->HttpSocket->request['header']['Authorization'], 'nc=00000003') > 0); - $this->assertEqual($this->HttpSocket->config['request']['auth']['nc'], 4); + $this->assertEqual($auth['nc'], 4); $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); $response2 = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); $this->assertNotEqual($response, $response2); @@ -178,8 +174,8 @@ class DigestAuthenticationTest extends CakeTestCase { public function testPathChanged() { $this->HttpSocket->nextHeader = 'Digest realm="The batcave",nonce="4cded326c6c51"'; $this->HttpSocket->request['uri']['path'] = '/admin'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $responsePos = strpos($this->HttpSocket->request['header']['Authorization'], 'response='); $response = substr($this->HttpSocket->request['header']['Authorization'], $responsePos + 10, 32); $this->assertNotEqual($response, 'da7e2a46b471d77f70a9bb3698c8902b'); @@ -193,8 +189,8 @@ class DigestAuthenticationTest extends CakeTestCase { public function testNoDigestResponse() { $this->HttpSocket->nextHeader = false; $this->HttpSocket->request['uri']['path'] = '/admin'; - $this->HttpSocket->config['request']['auth'] = array(); - DigestAuthentication::authentication($this->HttpSocket); + $auth = array('user' => 'admin', 'pass' => '1234'); + DigestAuthentication::authentication($this->HttpSocket, $auth); $this->assertFalse(isset($this->HttpSocket->request['header']['Authorization'])); } From 075bdebebe7c04ffa6d5d00b9a9cba4bc9809da3 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 3 Dec 2010 23:10:07 -0200 Subject: [PATCH 25/52] Moved the proxy configuration from request to class attribute. --- cake/libs/http/basic_authentication.php | 7 +-- cake/libs/http_socket.php | 60 ++++++++++++++-------- cake/tests/cases/libs/http_socket.test.php | 52 ++++--------------- 3 files changed, 51 insertions(+), 68 deletions(-) diff --git a/cake/libs/http/basic_authentication.php b/cake/libs/http/basic_authentication.php index 393f4a867..111246210 100644 --- a/cake/libs/http/basic_authentication.php +++ b/cake/libs/http/basic_authentication.php @@ -44,12 +44,13 @@ class BasicAuthentication { * Proxy Authentication * * @param HttpSocket $http + * @param array $proxyInfo * @return void * @see http://www.ietf.org/rfc/rfc2617.txt */ - public static function proxyAuthentication(HttpSocket $http) { - if (isset($http->request['proxy']['user'], $http->request['proxy']['pass'])) { - $http->request['header']['Proxy-Authorization'] = self::_generateHeader($http->request['proxy']['user'], $http->request['proxy']['pass']); + public static function proxyAuthentication(HttpSocket $http, &$proxyInfo) { + if (isset($proxyInfo['user'], $proxyInfo['pass'])) { + $http->request['header']['Proxy-Authorization'] = self::_generateHeader($proxyInfo['user'], $proxyInfo['pass']); } } diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index d5ed518ad..06df4ad1f 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -59,13 +59,6 @@ class HttpSocket extends CakeSocket { 'query' => null, 'fragment' => null ), - 'proxy' => array( - 'method' => 'Basic', - 'host' => null, - 'port' => 3128, - 'user' => null, - 'pass' => null - ), 'version' => '1.1', 'body' => '', 'line' => null, @@ -118,13 +111,6 @@ class HttpSocket extends CakeSocket { 'host' => 'localhost', 'port' => 80 ), - 'proxy' => array( - 'method' => 'Basic', - 'host' => null, - 'port' => 3128, - 'user' => null, - 'pass' => null - ), 'cookies' => array() ) ); @@ -145,6 +131,14 @@ class HttpSocket extends CakeSocket { */ protected $_auth = array(); +/** + * Proxy settings + * + * @var array + * @access protected + */ + protected $_proxy = array(); + /** * Build an HTTP Socket using the specified configuration. * @@ -182,7 +176,7 @@ class HttpSocket extends CakeSocket { /** * Set authentication settings * - * @param string $method Authentication method (ex. Basic, Digest). If empty, disable authentication + * @param string $method Authentication method (ie. Basic, Digest). If empty, disable authentication * @param mixed $user Username for authentication. Can be an array with settings to authentication class * @param string $pass Password for authentication * @return void @@ -199,6 +193,28 @@ class HttpSocket extends CakeSocket { $this->_auth = array($method => compact('user', 'pass')); } +/** + * Set proxy settings + * + * @param mixed $host Proxy host. Can be an array with settings to authentication class + * @param integer $port Port. Default 3128. + * @param string $method Proxy method (ie, Basic, Digest). If empty, disable proxy authentication + * @param string $user Username if your proxy need authentication + * @param string $pass Password to proxy authentication + * @return void + */ + public function setProxyConfig($host, $port = 3128, $method = null, $user = null, $pass = null) { + if (empty($host)) { + $this->_proxy = array(); + return; + } + if (is_array($host)) { + $this->_proxy = $host + array('host' => null); + return; + } + $this->_proxy = compact('host', 'port', 'method', 'user', 'pass'); + } + /** * Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this * method and provide a more granular interface. @@ -490,23 +506,23 @@ class HttpSocket extends CakeSocket { * @return void */ protected function _setProxyConfig() { - if (empty($this->request['proxy']['host'])) { + if (empty($this->_proxy) || !isset($this->_proxy['host'], $this->_proxy['port'])) { return; } - $this->config['host'] = $this->request['proxy']['host']; - $this->config['port'] = $this->request['proxy']['port']; + $this->config['host'] = $this->_proxy['host']; + $this->config['port'] = $this->_proxy['port']; - if (empty($this->request['proxy']['method']) || !isset($this->request['proxy']['user'], $this->request['proxy']['pass'])) { + if (empty($this->_proxy['method']) || !isset($this->_proxy['user'], $this->_proxy['pass'])) { return; } - $authClass = Inflector::camelize($this->request['proxy']['method']) . 'Authentication'; + $authClass = Inflector::camelize($this->_proxy['method']) . 'Authentication'; if (!App::import('Lib', 'http/' . $authClass)) { throw new Exception(__('Unknown authentication method for proxy.')); } if (!method_exists($authClass, 'proxyAuthentication')) { throw new Exception(sprintf(__('The %s do not support proxy authentication.'), $authClass)); } - call_user_func("$authClass::proxyAuthentication", $this); + call_user_func("$authClass::proxyAuthentication", $this, &$this->_proxy); } /** @@ -882,7 +898,7 @@ class HttpSocket extends CakeSocket { $request['uri'] = $this->_parseUri($request['uri']); $request = array_merge(array('method' => 'GET'), $request); - if (!empty($request['proxy']['host'])) { + if (!empty($this->_proxy['host'])) { $request['uri'] = $this->_buildUri($request['uri'], '%scheme://%host:%port/%path?%query'); } else { $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query'); diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 06b055928..834237934 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -42,10 +42,11 @@ class TestAuthentication { * proxyAuthentication method * * @param HttpSocket $http + * @param array $proxyInfo * @return void */ - public static function proxyAuthentication(HttpSocket $http) { - $http->request['header']['Proxy-Authorization'] = 'Test ' . $http->request['proxy']['user'] . '.' . $http->request['proxy']['pass']; + public static function proxyAuthentication(HttpSocket $http, &$proxyInfo) { + $http->request['header']['Proxy-Authorization'] = 'Test ' . $proxyInfo['user'] . '.' . $proxyInfo['pass']; } } @@ -287,13 +288,6 @@ class HttpSocketTest extends CakeTestCase { , 'host' => 'www.cakephp.org' , 'port' => 23 ), - 'proxy' => array( - 'method' => 'Basic', - 'host' => null, - 'port' => 3128, - 'user' => null, - 'pass' => null - ), 'cookies' => array(), ) ); @@ -318,13 +312,6 @@ class HttpSocketTest extends CakeTestCase { , 'host' => 'www.foo.com' , 'port' => 80 ), - 'proxy' => array( - 'method' => 'Basic', - 'host' => null, - 'port' => 3128, - 'user' => null, - 'pass' => null - ), 'cookies' => array() ) ); @@ -366,13 +353,6 @@ class HttpSocketTest extends CakeTestCase { , 'host' => 'www.cakephp.org' , 'port' => 80, ), - 'proxy' => array( - 'method' => 'Basic', - 'host' => null, - 'port' => 3128, - 'user' => null, - 'pass' => null - ), 'cookies' => array(), ), ) @@ -387,13 +367,6 @@ class HttpSocketTest extends CakeTestCase { , 'path' => '/' , 'query' => array('foo' => 'bar') , 'fragment' => null - ), - 'proxy' => array( - 'method' => 'Basic', - 'host' => null, - 'port' => 3128, - 'user' => null, - 'pass' => null ) , 'version' => '1.1' , 'body' => '' @@ -636,31 +609,24 @@ class HttpSocketTest extends CakeTestCase { $this->Socket->reset(); $this->Socket->expects($this->any())->method('connect')->will($this->returnValue(true)); $this->Socket->expects($this->any())->method('read')->will($this->returnValue(false)); - $request = array( - 'uri' => 'http://www.cakephp.org/', - 'proxy' => array( - 'host' => 'proxy.server', - 'port' => 123 - ) - ); + + $this->Socket->setProxyConfig('proxy.server', 123); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n"; - $this->Socket->request($request); + $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); $this->assertEqual($this->Socket->config['host'], 'proxy.server'); $this->assertEqual($this->Socket->config['port'], 123); - $request['proxy']['method'] = 'Test'; - $request['proxy']['user'] = 'mark'; - $request['proxy']['pass'] = 'secret'; $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; - $this->Socket->request($request); + $this->Socket->setProxyConfig('proxy.server', 123, 'Test', 'mark', 'secret'); + $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); $this->assertEqual($this->Socket->config['host'], 'proxy.server'); $this->assertEqual($this->Socket->config['port'], 123); $this->Socket->setAuthConfig('Test', 'login', 'passwd'); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nAuthorization: Test login.passwd\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; - $this->Socket->request($request); + $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); } From 874a1172a1b118d9a8d89569d7e6f9b2c1868698 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 3 Dec 2010 23:13:49 -0200 Subject: [PATCH 26/52] Cleaning auth and proxy configuration in full reset. --- cake/libs/http_socket.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 06df4ad1f..22fdf45d4 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -1132,6 +1132,8 @@ class HttpSocket extends CakeSocket { $this->response = $initalState['response']; return true; } + $this->_auth = array(); + $this->_proxy = array(); parent::reset($initalState); return true; } From 3f910dc6c1581594131cad74bf70414390400226 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 4 Dec 2010 00:58:49 -0200 Subject: [PATCH 27/52] Formatting --- cake/libs/http_socket.php | 69 +++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 22fdf45d4..1738c4076 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -37,7 +37,6 @@ class HttpSocket extends CakeSocket { * will be disabled and additional measures to deal with non-standard responses will be enabled. * * @var boolean - * @access public */ public $quirksMode = false; @@ -45,7 +44,6 @@ class HttpSocket extends CakeSocket { * The default values to use for a request * * @var array - * @access public */ public $request = array( 'method' => 'GET', @@ -71,11 +69,10 @@ class HttpSocket extends CakeSocket { ); /** -* The default structure for storing the response -* -* @var array -* @access public -*/ + * The default structure for storing the response + * + * @var array + */ public $response = array( 'raw' => array( 'status-line' => null, @@ -97,7 +94,6 @@ class HttpSocket extends CakeSocket { * Default configuration settings for the HttpSocket * * @var array - * @access public */ public $config = array( 'persistent' => false, @@ -119,7 +115,6 @@ class HttpSocket extends CakeSocket { * String that represents a line break. * * @var string - * @access public */ public $lineBreak = "\r\n"; @@ -127,7 +122,6 @@ class HttpSocket extends CakeSocket { * Authentication settings * * @var array - * @access protected */ protected $_auth = array(); @@ -135,7 +129,6 @@ class HttpSocket extends CakeSocket { * Proxy settings * * @var array - * @access protected */ protected $_proxy = array(); @@ -425,7 +418,7 @@ class HttpSocket extends CakeSocket { } /** - * Normalizes urls into a $uriTemplate. If no template is provided + * Normalizes urls into a $uriTemplate. If no template is provided * a default one will be used. Will generate the url using the * current config information. * @@ -457,10 +450,10 @@ class HttpSocket extends CakeSocket { } if (is_string($url)) { if ($url{0} == '/') { - $url = $this->config['request']['uri']['host'].':'.$this->config['request']['uri']['port'] . $url; + $url = $this->config['request']['uri']['host'] . ':' . $this->config['request']['uri']['port'] . $url; } if (!preg_match('/^.+:\/\/|\*|^\//', $url)) { - $url = $this->config['request']['uri']['scheme'].'://'.$url; + $url = $this->config['request']['uri']['scheme'] . '://' . $url; } } elseif (!is_array($url) && !empty($url)) { return false; @@ -504,6 +497,7 @@ class HttpSocket extends CakeSocket { * Set the proxy configuration and authentication * * @return void + * @throws Exception */ protected function _setProxyConfig() { if (empty($this->_proxy) || !isset($this->_proxy['host'], $this->_proxy['port'])) { @@ -570,7 +564,7 @@ class HttpSocket extends CakeSocket { $response['body'] = $decoded['body']; if (!empty($decoded['header'])) { - $response['header'] = $this->_parseHeader($this->_buildHeader($response['header']).$this->_buildHeader($decoded['header'])); + $response['header'] = $this->_parseHeader($this->_buildHeader($response['header']) . $this->_buildHeader($decoded['header'])); } if (!empty($response['header'])) { @@ -601,7 +595,7 @@ class HttpSocket extends CakeSocket { if (empty($encoding)) { return array('body' => $body, 'header' => false); } - $decodeMethod = '_decode'.Inflector::camelize(str_replace('-', '_', $encoding)).'Body'; + $decodeMethod = '_decode'.Inflector::camelize(str_replace('-', '_', $encoding)) . 'Body'; if (!is_callable(array(&$this, $decodeMethod))) { if (!$this->quirksMode) { @@ -663,7 +657,7 @@ class HttpSocket extends CakeSocket { } $decodedBody .= $chunk; if ($chunkLength !== 0) { - $body = substr($body, $chunkLength+strlen("\r\n")); + $body = substr($body, $chunkLength + strlen("\r\n")); } } @@ -707,7 +701,7 @@ class HttpSocket extends CakeSocket { /** * Takes a $uri array and turns it into a fully qualified URL string * - * @param mixed $uri Either A $uri array, or a request string. Will use $this->config if left empty. + * @param mixed $uri Either A $uri array, or a request string. Will use $this->config if left empty. * @param string $uriTemplate The Uri template/format to use. * @return mixed A fully qualified URL formated according to $uriTemplate, or false on failure */ @@ -741,7 +735,7 @@ class HttpSocket extends CakeSocket { $uriTemplate = str_replace(':%port', null, $uriTemplate); } foreach ($uri as $property => $value) { - $uriTemplate = str_replace('%'.$property, $value, $uriTemplate); + $uriTemplate = str_replace('%' . $property, $value, $uriTemplate); } if ($uriTemplate === '/*') { @@ -816,7 +810,7 @@ class HttpSocket extends CakeSocket { * - ?key[subKey]=value * - ?key[]=value1&key[]=value2 * - * A leading '?' mark in $query is optional and does not effect the outcome of this function. + * A leading '?' mark in $query is optional and does not effect the outcome of this function. * For the complete capabilities of this implementation take a look at HttpSocketTest::testparseQuery() * * @param mixed $query A query string to parse into an array or an array to return directly "as is" @@ -907,7 +901,7 @@ class HttpSocket extends CakeSocket { if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { throw new Exception(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.'), join(',', $asteriskMethods))); } - return $request['method'].' '.$request['uri'].' '.$versionToken.$this->lineBreak; + return $request['method'] . ' ' . $request['uri'] . ' ' . $versionToken . $this->lineBreak; } /** @@ -960,7 +954,7 @@ class HttpSocket extends CakeSocket { $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content); $field = $this->_escapeToken($field); - $returnHeader .= $field.': '.$contents.$this->lineBreak; + $returnHeader .= $field . ': ' . $contents . $this->lineBreak; } } return $returnHeader; @@ -1004,10 +998,9 @@ class HttpSocket extends CakeSocket { * * @param array $header Header array containing one ore more 'Set-Cookie' headers. * @return mixed Either false on no cookies, or an array of cookies recieved. - * @access public * @todo Make this 100% RFC 2965 confirm */ - function parseCookies($header) { + public function parseCookies($header) { if (!isset($header['Set-Cookie'])) { return false; } @@ -1016,7 +1009,7 @@ class HttpSocket extends CakeSocket { foreach ((array)$header['Set-Cookie'] as $cookie) { if (strpos($cookie, '";"') !== false) { $cookie = str_replace('";"', "{__cookie_replace__}", $cookie); - $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie)); + $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie)); } else { $parts = preg_split('/\;[ \t]*/', $cookie); } @@ -1046,28 +1039,26 @@ class HttpSocket extends CakeSocket { * * @param array $cookies Array of cookies to send with the request. * @return string Cookie header string to be sent with the request. - * @access public * @todo Refactor token escape mechanism to be configurable */ - function buildCookies($cookies) { + public function buildCookies($cookies) { $header = array(); foreach ($cookies as $name => $cookie) { - $header[] = $name.'='.$this->_escapeToken($cookie['value'], array(';')); + $header[] = $name . '=' . $this->_escapeToken($cookie['value'], array(';')); } - $header = $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic'); - return $header; + return $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic'); } /** * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs) * * @param string $token Token to unescape + * @param array $chars * @return string Unescaped token - * @access protected * @todo Test $chars parameter */ - function _unescapeToken($token, $chars = null) { - $regex = '/"(['.join('', $this->_tokenEscapeChars(true, $chars)).'])"/'; + protected function _unescapeToken($token, $chars = null) { + $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/'; $token = preg_replace($regex, '\\1', $token); return $token; } @@ -1076,12 +1067,12 @@ class HttpSocket extends CakeSocket { * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs) * * @param string $token Token to escape + * @param array $chars * @return string Escaped token - * @access protected * @todo Test $chars parameter */ - function _escapeToken($token, $chars = null) { - $regex = '/(['.join('', $this->_tokenEscapeChars(true, $chars)).'])/'; + protected function _escapeToken($token, $chars = null) { + $regex = '/([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])/'; $token = preg_replace($regex, '"\\1"', $token); return $token; } @@ -1090,11 +1081,11 @@ class HttpSocket extends CakeSocket { * Gets escape chars according to RFC 2616 (HTTP 1.1 specs). * * @param boolean $hex true to get them as HEX values, false otherwise + * @param array $chars * @return array Escape chars - * @access protected * @todo Test $chars parameter */ - function _tokenEscapeChars($hex = true, $chars = null) { + protected function _tokenEscapeChars($hex = true, $chars = null) { if (!empty($chars)) { $escape = $chars; } else { @@ -1110,7 +1101,7 @@ class HttpSocket extends CakeSocket { } $regexChars = ''; foreach ($escape as $key => $char) { - $escape[$key] = '\\x'.str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT); + $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT); } return $escape; } From 123b2256c5ff5936a03fe5e47463cd5e35f6194b Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 4 Dec 2010 01:23:07 -0200 Subject: [PATCH 28/52] Formatting --- cake/tests/cases/libs/http_socket.test.php | 502 +++++++++++---------- 1 file changed, 255 insertions(+), 247 deletions(-) diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 834237934..9bfa3bf96 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -51,6 +51,10 @@ class TestAuthentication { } +/** + * TestHttpSocket + * + */ class TestHttpSocket extends HttpSocket { /** @@ -59,7 +63,7 @@ class TestHttpSocket extends HttpSocket { * @param mixed $uri URI (see {@link _parseUri()}) * @return array Current configuration settings */ - function configUri($uri = null) { + public function configUri($uri = null) { return parent::_configUri($uri); } @@ -70,7 +74,7 @@ class TestHttpSocket extends HttpSocket { * @param mixed $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc. * @return array Parsed URI */ - function parseUri($uri = null, $base = array()) { + public function parseUri($uri = null, $base = array()) { return parent::_parseUri($uri, $base); } @@ -81,7 +85,7 @@ class TestHttpSocket extends HttpSocket { * @param string $uriTemplate The Uri template/format to use * @return string A fully qualified URL formated according to $uriTemplate */ - function buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') { + public function buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') { return parent::_buildUri($uri, $uriTemplate); } @@ -91,7 +95,7 @@ class TestHttpSocket extends HttpSocket { * @param array $header Header to build * @return string Header built from array */ - function buildHeader($header, $mode = 'standard') { + public function buildHeader($header, $mode = 'standard') { return parent::_buildHeader($header, $mode); } @@ -101,7 +105,7 @@ class TestHttpSocket extends HttpSocket { * @param string $message Message to parse * @return array Parsed message (with indexed elements such as raw, status, header, body) */ - function parseResponse($message) { + public function parseResponse($message) { return parent::_parseResponse($message); } @@ -111,7 +115,7 @@ class TestHttpSocket extends HttpSocket { * @param array $header Header as an indexed array (field => value) * @return array Parsed header */ - function parseHeader($header) { + public function parseHeader($header) { return parent::_parseHeader($header); } @@ -121,7 +125,7 @@ class TestHttpSocket extends HttpSocket { * @param mixed $query A query string to parse into an array or an array to return directly "as is" * @return array The $query parsed into a possibly multi-level array. If an empty $query is given, an empty array is returned. */ - function parseQuery($query) { + public function parseQuery($query) { return parent::_parseQuery($query); } @@ -132,7 +136,7 @@ class TestHttpSocket extends HttpSocket { * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding * @return mixed Array or false */ - function decodeBody($body, $encoding = 'chunked') { + public function decodeBody($body, $encoding = 'chunked') { return parent::_decodeBody($body, $encoding); } @@ -142,7 +146,7 @@ class TestHttpSocket extends HttpSocket { * @param string $body A string continaing the chunked body to decode * @return mixed Array or false */ - function decodeChunkedBody($body) { + public function decodeChunkedBody($body) { return parent::_decodeChunkedBody($body); } @@ -153,7 +157,7 @@ class TestHttpSocket extends HttpSocket { * @param string $versionToken The version token to use, defaults to HTTP/1.1 * @return string Request line */ - function buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { + public function buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { return parent::_buildRequestLine($request, $versionToken); } @@ -163,7 +167,7 @@ class TestHttpSocket extends HttpSocket { * @param boolean $hex true to get them as HEX values, false otherwise * @return array Escape chars */ - function tokenEscapeChars($hex = true, $chars = null) { + public function tokenEscapeChars($hex = true, $chars = null) { return parent::_tokenEscapeChars($hex, $chars); } @@ -173,7 +177,7 @@ class TestHttpSocket extends HttpSocket { * @param string $token Token to escape * @return string Escaped token */ - function EscapeToken($token, $chars = null) { + public function EscapeToken($token, $chars = null) { return parent::_escapeToken($token, $chars); } @@ -183,7 +187,7 @@ class TestHttpSocket extends HttpSocket { * @param string $token Token to unescape * @return string Unescaped token */ - function unescapeToken($token, $chars = null) { + public function unescapeToken($token, $chars = null) { return parent::_unescapeToken($token, $chars); } } @@ -200,7 +204,6 @@ class HttpSocketTest extends CakeTestCase { * Socket property * * @var mixed null - * @access public */ public $Socket = null; @@ -208,17 +211,15 @@ class HttpSocketTest extends CakeTestCase { * RequestSocket property * * @var mixed null - * @access public */ public $RequestSocket = null; /** * This function sets up a TestHttpSocket instance we are going to use for testing * - * @access public * @return void */ - function setUp() { + public function setUp() { if (!class_exists('MockHttpSocket')) { $this->getMock('TestHttpSocket', array('read', 'write', 'connect'), array(), 'MockHttpSocket'); $this->getMock('TestHttpSocket', array('read', 'write', 'connect', 'request'), array(), 'MockHttpSocketRequests'); @@ -231,20 +232,18 @@ class HttpSocketTest extends CakeTestCase { /** * We use this function to clean up after the test case was executed * - * @access public * @return void */ - function tearDown() { + public function tearDown() { unset($this->Socket, $this->RequestSocket); } /** * Test that HttpSocket::__construct does what one would expect it to do * - * @access public * @return void */ - function testConstruct() { + public function testConstruct() { $this->Socket->reset(); $baseConfig = $this->Socket->config; $this->Socket->expects($this->never())->method('connect'); @@ -270,25 +269,24 @@ class HttpSocketTest extends CakeTestCase { /** * Test that HttpSocket::configUri works properly with different types of arguments * - * @access public * @return void */ - function testConfigUri() { + public function testConfigUri() { $this->Socket->reset(); $r = $this->Socket->configUri('https://bob:secret@www.cakephp.org:23/?query=foo'); $expected = array( 'persistent' => false, - 'host' => 'www.cakephp.org', - 'protocol' => 'tcp', - 'port' => 23, - 'timeout' => 30, + 'host' => 'www.cakephp.org', + 'protocol' => 'tcp', + 'port' => 23, + 'timeout' => 30, 'request' => array( 'uri' => array( - 'scheme' => 'https' - , 'host' => 'www.cakephp.org' - , 'port' => 23 + 'scheme' => 'https', + 'host' => 'www.cakephp.org', + 'port' => 23 ), - 'cookies' => array(), + 'cookies' => array() ) ); $this->assertEquals($this->Socket->config, $expected); @@ -302,15 +300,15 @@ class HttpSocketTest extends CakeTestCase { $r = $this->Socket->configUri('http://www.foo.com'); $expected = array( 'persistent' => false, - 'host' => 'www.foo.com', - 'protocol' => 'tcp', - 'port' => 80, - 'timeout' => 30, + 'host' => 'www.foo.com', + 'protocol' => 'tcp', + 'port' => 80, + 'timeout' => 30, 'request' => array( 'uri' => array( - 'scheme' => 'http' - , 'host' => 'www.foo.com' - , 'port' => 80 + 'scheme' => 'http', + 'host' => 'www.foo.com', + 'port' => 80 ), 'cookies' => array() ) @@ -328,191 +326,216 @@ class HttpSocketTest extends CakeTestCase { /** * Tests that HttpSocket::request (the heart of the HttpSocket) is working properly. * - * @access public * @return void */ - function testRequest() { + public function testRequest() { $this->Socket->reset(); $response = $this->Socket->request(true); $this->assertFalse($response); $tests = array( - 0 => array( - 'request' => 'http://www.cakephp.org/?foo=bar' - , 'expectation' => array( + array( + 'request' => 'http://www.cakephp.org/?foo=bar', + 'expectation' => array( 'config' => array( - 'persistent' => false - , 'host' => 'www.cakephp.org' - , 'protocol' => 'tcp' - , 'port' => 80 - , 'timeout' => 30 - , 'request' => array( + 'persistent' => false, + 'host' => 'www.cakephp.org', + 'protocol' => 'tcp', + 'port' => 80, + 'timeout' => 30, + 'request' => array( 'uri' => array ( - 'scheme' => 'http' - , 'host' => 'www.cakephp.org' - , 'port' => 80, + 'scheme' => 'http', + 'host' => 'www.cakephp.org', + 'port' => 80 ), - 'cookies' => array(), - ), - ) - , 'request' => array( - 'method' => 'GET' - , 'uri' => array( - 'scheme' => 'http' - , 'host' => 'www.cakephp.org' - , 'port' => 80 - , 'user' => null - , 'pass' => null - , 'path' => '/' - , 'query' => array('foo' => 'bar') - , 'fragment' => null + 'cookies' => array() ) - , 'version' => '1.1' - , 'body' => '' - , 'line' => "GET /?foo=bar HTTP/1.1\r\n" - , 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n" - , 'raw' => "" - , 'cookies' => array(), + ), + 'request' => array( + 'method' => 'GET', + 'uri' => array( + 'scheme' => 'http', + 'host' => 'www.cakephp.org', + 'port' => 80, + 'user' => null, + 'pass' => null, + 'path' => '/', + 'query' => array('foo' => 'bar'), + 'fragment' => null + ), + 'version' => '1.1', + 'body' => '', + 'line' => "GET /?foo=bar HTTP/1.1\r\n", + 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n", + 'raw' => "", + 'cookies' => array() ) ) - ) - , 1 => array( + ), + array( 'request' => array( 'uri' => array( - 'host' => 'www.cakephp.org' - , 'query' => '?foo=bar' + 'host' => 'www.cakephp.org', + 'query' => '?foo=bar' ) ) - ) - , 2 => array( + ), + array( 'request' => 'www.cakephp.org/?foo=bar' - ) - , 3 => array( - 'request' => array('host' => '192.168.0.1', 'uri' => 'http://www.cakephp.org/?foo=bar') - , 'expectation' => array( + ), + array( + 'request' => array( + 'host' => '192.168.0.1', + 'uri' => 'http://www.cakephp.org/?foo=bar' + ), + 'expectation' => array( 'request' => array( 'uri' => array('host' => 'www.cakephp.org') - ) - , 'config' => array( + ), + 'config' => array( 'request' => array( 'uri' => array('host' => 'www.cakephp.org') - ) - , 'host' => '192.168.0.1' + ), + 'host' => '192.168.0.1' ) ) - ) - , 'reset4' => array( + ), + 'reset4' => array( 'request.uri.query' => array() - ) - , 4 => array( - 'request' => array('header' => array('Foo@woo' => 'bar-value')) - , 'expectation' => array( + ), + array( + 'request' => array( + 'header' => array('Foo@woo' => 'bar-value') + ), + 'expectation' => array( 'request' => array( - 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n" - , 'line' => "GET / HTTP/1.1\r\n" + 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n", + 'line' => "GET / HTTP/1.1\r\n" ) ) - ) - , 5 => array( - 'request' => array('header' => array('Foo@woo' => 'bar-value', 'host' => 'foo.com'), 'uri' => 'http://www.cakephp.org/') - , 'expectation' => array( + ), + array( + 'request' => array('header' => array('Foo@woo' => 'bar-value', 'host' => 'foo.com'), 'uri' => 'http://www.cakephp.org/'), + 'expectation' => array( 'request' => array( 'header' => "Host: foo.com\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n" - ) - , 'config' => array( + ), + 'config' => array( 'host' => 'www.cakephp.org' ) ) - ) - , 6 => array( - 'request' => array('header' => "Foo: bar\r\n") - , 'expectation' => array( + ), + array( + 'request' => array('header' => "Foo: bar\r\n"), + 'expectation' => array( 'request' => array( 'header' => "Foo: bar\r\n" ) ) - ) - , 7 => array( - 'request' => array('header' => "Foo: bar\r\n", 'uri' => 'http://www.cakephp.org/search?q=http_socket#ignore-me') - , 'expectation' => array( + ), + array( + 'request' => array('header' => "Foo: bar\r\n", 'uri' => 'http://www.cakephp.org/search?q=http_socket#ignore-me'), + 'expectation' => array( 'request' => array( 'uri' => array( - 'path' => '/search' - , 'query' => array('q' => 'http_socket') - , 'fragment' => 'ignore-me' - ) - , 'line' => "GET /search?q=http_socket HTTP/1.1\r\n" + 'path' => '/search', + 'query' => array('q' => 'http_socket'), + 'fragment' => 'ignore-me' + ), + 'line' => "GET /search?q=http_socket HTTP/1.1\r\n" ) ) - ) - , 'reset8' => array( + ), + 'reset8' => array( 'request.uri.query' => array() - ) - , 8 => array( - 'request' => array('method' => 'POST', 'uri' => 'http://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today')) - , 'expectation' => array( + ), + array( + 'request' => array( + 'method' => 'POST', + 'uri' => 'http://www.cakephp.org/posts/add', + 'body' => array( + 'name' => 'HttpSocket-is-released', + 'date' => 'today' + ) + ), + 'expectation' => array( 'request' => array( - 'method' => 'POST' - , 'uri' => array( - 'path' => '/posts/add' - , 'fragment' => null - ) - , 'body' => "name=HttpSocket-is-released&date=today" - , 'line' => "POST /posts/add HTTP/1.1\r\n" - , 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n" - , 'raw' => "name=HttpSocket-is-released&date=today" + 'method' => 'POST', + 'uri' => array( + 'path' => '/posts/add', + 'fragment' => null + ), + 'body' => "name=HttpSocket-is-released&date=today", + 'line' => "POST /posts/add HTTP/1.1\r\n", + 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n", + 'raw' => "name=HttpSocket-is-released&date=today" ) ) - ) - , 9 => array( - 'request' => array('method' => 'POST', 'uri' => 'http://www.cakephp.org:8080/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today')) - , 'expectation' => array( + ), + array( + 'request' => array( + 'method' => 'POST', + 'uri' => 'http://www.cakephp.org:8080/posts/add', + 'body' => array( + 'name' => 'HttpSocket-is-released', + 'date' => 'today' + ) + ), + 'expectation' => array( 'config' => array( - 'port' => 8080 - , 'request' => array( + 'port' => 8080, + 'request' => array( 'uri' => array( 'port' => 8080 ) ) - ) - , 'request' => array( + ), + 'request' => array( 'uri' => array( 'port' => 8080 - ) - , 'header' => "Host: www.cakephp.org:8080\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n" + ), + 'header' => "Host: www.cakephp.org:8080\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n" ) ) - ) - , 10 => array( - 'request' => array('method' => 'POST', 'uri' => 'https://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today')) - , 'expectation' => array( + ), + array( + 'request' => array( + 'method' => 'POST', + 'uri' => 'https://www.cakephp.org/posts/add', + 'body' => array( + 'name' => 'HttpSocket-is-released', + 'date' => 'today' + ) + ), + 'expectation' => array( 'config' => array( - 'port' => 443 - , 'request' => array( + 'port' => 443, + 'request' => array( 'uri' => array( - 'scheme' => 'https' - , 'port' => 443 + 'scheme' => 'https', + 'port' => 443 ) ) - ) - , 'request' => array( + ), + 'request' => array( 'uri' => array( - 'scheme' => 'https' - , 'port' => 443 - ) - , 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n" + 'scheme' => 'https', + 'port' => 443 + ), + 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n" ) ) - ) - , 11 => array( + ), + array( 'request' => array( - 'method' => 'POST', - 'uri' => 'https://www.cakephp.org/posts/add', - 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'), - 'cookies' => array('foo' => array('value' => 'bar')) - ) - , 'expectation' => array( + 'method' => 'POST', + 'uri' => 'https://www.cakephp.org/posts/add', + 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'), + 'cookies' => array('foo' => array('value' => 'bar')) + ), + 'expectation' => array( 'request' => array( 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\nCookie: foo=bar\r\n", 'cookies' => array( @@ -538,10 +561,10 @@ class HttpSocketTest extends CakeTestCase { $this->Socket->request($test['request']); $raw = $expectation['request']['raw']; - $expectation['request']['raw'] = $expectation['request']['line'].$expectation['request']['header']."\r\n".$raw; + $expectation['request']['raw'] = $expectation['request']['line'] . $expectation['request']['header'] . "\r\n" . $raw; $r = array('config' => $this->Socket->config, 'request' => $this->Socket->request); - $v = $this->assertEquals($r, $expectation, 'Failed test #'.$i.' '); + $v = $this->assertEquals($r, $expectation, 'Failed test #' . $i . ' '); $expectation['request']['raw'] = $raw; } @@ -560,10 +583,9 @@ class HttpSocketTest extends CakeTestCase { /** * testRequest2 method * - * @access public * @return void */ - function testRequest2() { + public function testRequest2() { $this->Socket->reset(); $request = array('uri' => 'htpp://www.cakephp.org/'); $number = mt_rand(0, 9999999); @@ -580,10 +602,9 @@ class HttpSocketTest extends CakeTestCase { /** * testRequest3 method * - * @access public * @return void */ - function testRequest3() { + public function testRequest3() { $request = array('uri' => 'htpp://www.cakephp.org/'); $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a cookie test!

"; $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); @@ -605,7 +626,7 @@ class HttpSocketTest extends CakeTestCase { * * @return void */ - function testProxy() { + public function testProxy() { $this->Socket->reset(); $this->Socket->expects($this->any())->method('connect')->will($this->returnValue(true)); $this->Socket->expects($this->any())->method('read')->will($this->returnValue(false)); @@ -633,10 +654,9 @@ class HttpSocketTest extends CakeTestCase { /** * testUrl method * - * @access public * @return void */ - function testUrl() { + public function testUrl() { $this->Socket->reset(true); $this->assertEquals($this->Socket->url(true), false); @@ -688,10 +708,9 @@ class HttpSocketTest extends CakeTestCase { /** * testGet method * - * @access public * @return void */ - function testGet() { + public function testGet() { $this->RequestSocket->reset(); $this->RequestSocket->expects($this->at(0)) @@ -701,7 +720,7 @@ class HttpSocketTest extends CakeTestCase { $this->RequestSocket->expects($this->at(1)) ->method('request') ->with(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=bar')); - + $this->RequestSocket->expects($this->at(2)) ->method('request') ->with(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=bar')); @@ -745,7 +764,7 @@ class HttpSocketTest extends CakeTestCase { * * @return void */ - function testConsecutiveGetResetsAuthCredentials() { + public function testConsecutiveGetResetsAuthCredentials() { $socket = new MockHttpSocket(); $socket->get('http://mark:secret@example.com/test'); $this->assertEqual($socket->request['uri']['user'], 'mark'); @@ -762,10 +781,9 @@ class HttpSocketTest extends CakeTestCase { /** * testPostPutDelete method * - * @access public * @return void */ - function testPost() { + public function testPost() { $this->RequestSocket->reset(); $this->RequestSocket->expects($this->at(0)) ->method('request') @@ -784,7 +802,12 @@ class HttpSocketTest extends CakeTestCase { $this->RequestSocket->post('http://www.google.com/', null, array('line' => 'Hey Server')); } - function testPut() { +/** + * testPut + * + * @return void + */ + public function testPut() { $this->RequestSocket->reset(); $this->RequestSocket->expects($this->at(0)) ->method('request') @@ -803,7 +826,12 @@ class HttpSocketTest extends CakeTestCase { $this->RequestSocket->put('http://www.google.com/', null, array('line' => 'Hey Server')); } - function testDelete() { +/** + * testDelete + * + * @return void + */ + public function testDelete() { $this->RequestSocket->reset(); $this->RequestSocket->expects($this->at(0)) ->method('request') @@ -825,10 +853,9 @@ class HttpSocketTest extends CakeTestCase { /** * testParseResponse method * - * @access public * @return void */ - function testParseResponse() { + public function testParseResponse() { $this->Socket->reset(); $r = $this->Socket->parseResponse(array('foo' => 'bar')); @@ -846,8 +873,8 @@ class HttpSocketTest extends CakeTestCase { 'status-line' => "HTTP/1.x 200 OK\r\n", 'header' => "Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n", 'body' => "

Hello World

\r\n

It's good to be html

" - ) - , 'expectations' => array( + ), + 'expectations' => array( 'status.http-version' => 'HTTP/1.x', 'status.code' => 200, 'status.reason-phrase' => 'OK', @@ -858,9 +885,9 @@ class HttpSocketTest extends CakeTestCase { 'no-header' => array( 'response' => array( 'status-line' => "HTTP/1.x 404 OK\r\n", - 'header' => null, - ) - , 'expectations' => array( + 'header' => null + ), + 'expectations' => array( 'status.code' => 404, 'header' => array() ) @@ -897,19 +924,18 @@ class HttpSocketTest extends CakeTestCase { $expectations = array(); foreach ($tests as $name => $test) { - $testResponse = array_merge($testResponse, $test['response']); - $testResponse['response'] = $testResponse['status-line'].$testResponse['header']."\r\n".$testResponse['body']; + $testResponse['response'] = $testResponse['status-line'] . $testResponse['header'] . "\r\n" . $testResponse['body']; $r = $this->Socket->parseResponse($testResponse['response']); $expectations = array_merge($expectations, $test['expectations']); foreach ($expectations as $property => $expectedVal) { $val = Set::extract($r, $property); - $this->assertEquals($val, $expectedVal, 'Test "'.$name.'": response.'.$property.' - %s'); + $this->assertEquals($val, $expectedVal, 'Test "' . $name . '": response.' . $property . ' - %s'); } foreach (array('status-line', 'header', 'body', 'response') as $field) { - $this->assertEquals($r['raw'][$field], $testResponse[$field], 'Test response.raw.'.$field.': %s'); + $this->assertEquals($r['raw'][$field], $testResponse[$field], 'Test response.raw.' . $field . ': %s'); } } } @@ -917,10 +943,9 @@ class HttpSocketTest extends CakeTestCase { /** * testDecodeBody method * - * @access public * @return void */ - function testDecodeBody() { + public function testDecodeBody() { $this->Socket->reset(); $r = $this->Socket->decodeBody(true); @@ -939,7 +964,12 @@ class HttpSocketTest extends CakeTestCase { $this->assertEquals($r, $sample['decoded']); } - function testDecodeFooCoded() { +/** + * testDecodeFooCoded + * + * @return void + */ + public function testDecodeFooCoded() { $this->Socket->reset(); $r = $this->Socket->decodeBody(true); @@ -967,10 +997,9 @@ class HttpSocketTest extends CakeTestCase { /** * testDecodeChunkedBody method * - * @access public * @return void */ - function testDecodeChunkedBody() { + public function testDecodeChunkedBody() { $this->Socket->reset(); $r = $this->Socket->decodeChunkedBody(true); @@ -1023,10 +1052,9 @@ class HttpSocketTest extends CakeTestCase { /** * testBuildRequestLine method * - * @access public * @return void */ - function testBuildRequestLine() { + public function testBuildRequestLine() { $this->Socket->reset(); $this->Socket->quirksMode = true; @@ -1067,7 +1095,7 @@ class HttpSocketTest extends CakeTestCase { $request['method'] = 'GET'; $this->Socket->quirksMode = true; $r = $this->Socket->buildRequestLine($request); - $this->assertEquals($r, "GET * HTTP/1.1\r\n"); + $this->assertEquals($r, "GET * HTTP/1.1\r\n"); $r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n"); $this->assertEquals($r, "GET * HTTP/1.1\r\n"); @@ -1076,10 +1104,9 @@ class HttpSocketTest extends CakeTestCase { /** * testBadBuildRequestLine method * - * @access public * @return void */ - function testBadBuildRequestLine() { + public function testBadBuildRequestLine() { $this->expectError(); $r = $this->Socket->buildRequestLine('Foo'); } @@ -1087,10 +1114,9 @@ class HttpSocketTest extends CakeTestCase { /** * testBadBuildRequestLine2 method * - * @access public * @return void */ - function testBadBuildRequestLine2() { + public function testBadBuildRequestLine2() { $this->expectError(); $r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n"); } @@ -1098,10 +1124,9 @@ class HttpSocketTest extends CakeTestCase { /** * Asserts that HttpSocket::parseUri is working properly * - * @access public * @return void */ - function testParseUri() { + public function testParseUri() { $this->Socket->reset(); $uri = $this->Socket->parseUri(array('invalid' => 'uri-string')); @@ -1132,7 +1157,7 @@ class HttpSocketTest extends CakeTestCase { $this->assertEquals($uri, array( 'scheme' => 'http', 'host' => 'www.cakephp.org', - 'path' => '/', + 'path' => '/' )); $uri = $this->Socket->parseUri('http://www.cakephp.org', true); @@ -1197,7 +1222,7 @@ class HttpSocketTest extends CakeTestCase { $this->assertEquals($uri, array( 'scheme' => 'http', 'host' => 'www.google.com', - 'port' => 8080, + 'port' => 8080 )); $uri = $this->Socket->parseUri('http://www.cakephp.org/?param1=value1¶m2=value2%3Dvalue3'); @@ -1226,10 +1251,9 @@ class HttpSocketTest extends CakeTestCase { /** * Tests that HttpSocket::buildUri can turn all kinds of uri arrays (and strings) into fully or partially qualified URI's * - * @access public * @return void */ - function testBuildUri() { + public function testBuildUri() { $this->Socket->reset(); $r = $this->Socket->buildUri(true); @@ -1293,10 +1317,9 @@ class HttpSocketTest extends CakeTestCase { /** * Asserts that HttpSocket::parseQuery is working properly * - * @access public * @return void */ - function testParseQuery() { + public function testParseQuery() { $this->Socket->reset(); $query = $this->Socket->parseQuery(array('framework' => 'cakephp')); @@ -1345,16 +1368,10 @@ class HttpSocketTest extends CakeTestCase { $query = $this->Socket->parseQuery('a[][]=foo&a[bar]=php&a[][]=bar&a[][]=cake'); $expectedQuery = array( 'a' => array( - 0 => array( - 0 => 'foo' - ), + array('foo'), 'bar' => 'php', - 1 => array( - 0 => 'bar' - ), - array( - 0 => 'cake' - ) + array('bar'), + array('cake') ) ); $this->assertEquals($query, $expectedQuery); @@ -1400,10 +1417,9 @@ class HttpSocketTest extends CakeTestCase { * Tests that HttpSocket::buildHeader can turn a given $header array into a proper header string according to * HTTP 1.1 specs. * - * @access public * @return void */ - function testBuildHeader() { + public function testBuildHeader() { $this->Socket->reset(); $r = $this->Socket->buildHeader(true); @@ -1438,10 +1454,9 @@ class HttpSocketTest extends CakeTestCase { /** * Test that HttpSocket::parseHeader can take apart a given (and valid) $header string and turn it into an array. * - * @access public * @return void */ - function testParseHeader() { + public function testParseHeader() { $this->Socket->reset(); $r = $this->Socket->parseHeader(array('foo' => 'Bar', 'fOO-bAr' => 'quux')); @@ -1460,32 +1475,32 @@ class HttpSocketTest extends CakeTestCase { $header = "Date:Sat, 07 Apr 2007 10:10:25 GMT\r\nX-Powered-By: PHP/5.1.2\r\n"; $r = $this->Socket->parseHeader($header); $expected = array( - 'Date' => 'Sat, 07 Apr 2007 10:10:25 GMT' - , 'X-Powered-By' => 'PHP/5.1.2' + 'Date' => 'Sat, 07 Apr 2007 10:10:25 GMT', + 'X-Powered-By' => 'PHP/5.1.2' ); $this->assertEquals($r, $expected); $header = "people: Jim,John\r\nfoo-LAND: Bar\r\ncAKe-PHP: rocks\r\n"; $r = $this->Socket->parseHeader($header); $expected = array( - 'people' => 'Jim,John' - , 'foo-LAND' => 'Bar' - , 'cAKe-PHP' => 'rocks' + 'people' => 'Jim,John', + 'foo-LAND' => 'Bar', + 'cAKe-PHP' => 'rocks' ); $this->assertEquals($r, $expected); $header = "People: Jim,John,Tim\r\nPeople: Lisa,Tina,Chelsea\r\n"; $r = $this->Socket->parseHeader($header); $expected = array( - 'People' => array('Jim,John,Tim', 'Lisa,Tina,Chelsea') + 'People' => array('Jim,John,Tim', 'Lisa,Tina,Chelsea') ); $this->assertEquals($r, $expected); $header = "Multi-Line: I am a \r\nmulti line\t\r\nfield value.\r\nSingle-Line: I am not\r\n"; $r = $this->Socket->parseHeader($header); $expected = array( - 'Multi-Line' => "I am a\r\nmulti line\r\nfield value." - , 'Single-Line' => 'I am not' + 'Multi-Line' => "I am a\r\nmulti line\r\nfield value.", + 'Single-Line' => 'I am not' ); $this->assertEquals($r, $expected); @@ -1500,10 +1515,9 @@ class HttpSocketTest extends CakeTestCase { /** * testParseCookies method * - * @access public * @return void */ - function testParseCookies() { + public function testParseCookies() { $header = array( 'Set-Cookie' => array( 'foo=bar', @@ -1543,10 +1557,9 @@ class HttpSocketTest extends CakeTestCase { * testBuildCookies method * * @return void - * @access public * @todo Test more scenarios */ - function testBuildCookies() { + public function testBuildCookies() { $cookies = array( 'foo' => array( 'value' => 'bar' @@ -1564,10 +1577,9 @@ class HttpSocketTest extends CakeTestCase { /** * Tests that HttpSocket::_tokenEscapeChars() returns the right characters. * - * @access public * @return void */ - function testTokenEscapeChars() { + public function testTokenEscapeChars() { $this->Socket->reset(); $expected = array( @@ -1590,19 +1602,18 @@ class HttpSocketTest extends CakeTestCase { /** * Test that HttpSocket::escapeToken is escaping all characters as descriped in RFC 2616 (HTTP 1.1 specs) * - * @access public * @return void */ - function testEscapeToken() { + public function testEscapeToken() { $this->Socket->reset(); $this->assertEquals($this->Socket->escapeToken('Foo'), 'Foo'); $escape = $this->Socket->tokenEscapeChars(false); foreach ($escape as $char) { - $token = 'My-special-'.$char.'-Token'; + $token = 'My-special-' . $char . '-Token'; $escapedToken = $this->Socket->escapeToken($token); - $expectedToken = 'My-special-"'.$char.'"-Token'; + $expectedToken = 'My-special-"' . $char . '"-Token'; $this->assertEquals($escapedToken, $expectedToken, 'Test token escaping for ASCII '.ord($char)); } @@ -1616,19 +1627,18 @@ class HttpSocketTest extends CakeTestCase { /** * Test that escaped token strings are properly unescaped by HttpSocket::unescapeToken * - * @access public * @return void */ - function testUnescapeToken() { + public function testUnescapeToken() { $this->Socket->reset(); $this->assertEquals($this->Socket->unescapeToken('Foo'), 'Foo'); $escape = $this->Socket->tokenEscapeChars(false); foreach ($escape as $char) { - $token = 'My-special-"'.$char.'"-Token'; + $token = 'My-special-"' . $char . '"-Token'; $unescapedToken = $this->Socket->unescapeToken($token); - $expectedToken = 'My-special-'.$char.'-Token'; + $expectedToken = 'My-special-' . $char . '-Token'; $this->assertEquals($unescapedToken, $expectedToken, 'Test token unescaping for ASCII '.ord($char)); } @@ -1643,10 +1653,9 @@ class HttpSocketTest extends CakeTestCase { * This tests asserts HttpSocket::reset() resets a HttpSocket instance to it's initial state (before Object::__construct * got executed) * - * @access public * @return void */ - function testReset() { + public function testReset() { $this->Socket->reset(); $initialState = get_class_vars('HttpSocket'); @@ -1667,10 +1676,9 @@ class HttpSocketTest extends CakeTestCase { * This tests asserts HttpSocket::reset(false) resets certain HttpSocket properties to their initial state (before * Object::__construct got executed). * - * @access public * @return void */ - function testPartialReset() { + public function testPartialReset() { $this->Socket->reset(); $partialResetProperties = array('request', 'response'); From c4743a24388ccca519e223fea5fc810dc1f03ca1 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 4 Dec 2010 01:41:45 -0200 Subject: [PATCH 29/52] Changing the test with get to use version instead auth. --- cake/tests/cases/libs/http_socket.test.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 9bfa3bf96..858a5df10 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -731,13 +731,13 @@ class HttpSocketTest extends CakeTestCase { $this->RequestSocket->expects($this->at(4)) ->method('request') - ->with(array('method' => 'GET', 'uri' => 'http://www.google.com/', 'auth' => array('user' => 'foo', 'pass' => 'bar'))); + ->with(array('method' => 'GET', 'uri' => 'http://www.google.com/', 'version' => '1.0')); $this->RequestSocket->get('http://www.google.com/'); $this->RequestSocket->get('http://www.google.com/', array('foo' => 'bar')); $this->RequestSocket->get('http://www.google.com/', 'foo=bar'); $this->RequestSocket->get('http://www.google.com/?foo=bar', array('foobar' => '42', 'foo' => '23')); - $this->RequestSocket->get('http://www.google.com/', null, array('auth' => array('user' => 'foo', 'pass' => 'bar'))); + $this->RequestSocket->get('http://www.google.com/', null, array('version' => '1.0')); } /** From 97fe32f87c16a6a3e909389e58bac44de5a4960d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 5 Dec 2010 23:36:28 -0200 Subject: [PATCH 30/52] Request return a pointer to body. It will reduce the memory usage in big responses. --- cake/libs/http_socket.php | 15 ++++++++------- cake/tests/cases/libs/http_socket.test.php | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 1738c4076..c467393c9 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -213,15 +213,16 @@ class HttpSocket extends CakeSocket { * method and provide a more granular interface. * * @param mixed $request Either an URI string, or an array defining host/uri - * @return mixed false on error, request body on success + * @return mixed null on error, reference to request body on success */ - public function request($request = array()) { + public function &request($request = array()) { $this->reset(false); if (is_string($request)) { $request = array('uri' => $request); } elseif (!is_array($request)) { - return false; + $return = false; + return $return; } if (!isset($request['uri'])) { @@ -354,7 +355,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request, either false on failure or the response to the request. */ - public function get($uri = null, $query = array(), $request = array()) { + public function &get($uri = null, $query = array(), $request = array()) { if (!empty($query)) { $uri = $this->_parseUri($uri); if (isset($uri['query'])) { @@ -386,7 +387,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request, either false on failure or the response to the request. */ - public function post($uri = null, $data = array(), $request = array()) { + public function &post($uri = null, $data = array(), $request = array()) { $request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request); return $this->request($request); } @@ -399,7 +400,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request */ - public function put($uri = null, $data = array(), $request = array()) { + public function &put($uri = null, $data = array(), $request = array()) { $request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request); return $this->request($request); } @@ -412,7 +413,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request */ - public function delete($uri = null, $data = array(), $request = array()) { + public function &delete($uri = null, $data = array(), $request = array()) { $request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request); return $this->request($request); } diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 858a5df10..df4914ec3 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -621,6 +621,22 @@ class HttpSocketTest extends CakeTestCase { $this->assertFalse($this->Socket->connected); } +/** + * testRequestResultAsPointer method + * + * @return void + */ + public function testRequestResultAsPointer() { + $request = array('uri' => 'htpp://www.cakephp.org/'); + $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a cookie test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->connected = true; + $data =& $this->Socket->request($request); + $this->assertEqual($data, $this->Socket->response['body']); + $data = 'new data'; + $this->assertEqual($data, $this->Socket->response['body']); + } + /** * testProxy method * From 151ea2804f64531207e2452a07c80fd03758edb6 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 6 Dec 2010 03:23:09 -0200 Subject: [PATCH 31/52] Default value to raw key, avoiding warnings if line is false. --- cake/libs/http_socket.php | 1 + 1 file changed, 1 insertion(+) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index c467393c9..99bd71014 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -303,6 +303,7 @@ class HttpSocket extends CakeSocket { return $this->response = false; } + $this->request['raw'] = ''; if ($this->request['line'] !== false) { $this->request['raw'] = $this->request['line']; } From 33bb253dfa5c8d10a0774110ed72b8964b2e80b8 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 6 Dec 2010 03:56:16 -0200 Subject: [PATCH 32/52] Minor optimization in HttpSocket::reset(). --- cake/libs/http_socket.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 99bd71014..f88a16e67 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -1120,13 +1120,11 @@ class HttpSocket extends CakeSocket { if (empty($initalState)) { $initalState = get_class_vars(__CLASS__); } - if ($full == false) { + if (!$full) { $this->request = $initalState['request']; $this->response = $initalState['response']; return true; } - $this->_auth = array(); - $this->_proxy = array(); parent::reset($initalState); return true; } From 30a70b700bb0967c1f726bab809a6a2cf4834546 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 6 Dec 2010 04:02:23 -0200 Subject: [PATCH 33/52] HttpSocket::_configUri() always change config attribute and it is public. This function dont need return it. --- cake/libs/http_socket.php | 4 ++-- cake/tests/cases/libs/http_socket.test.php | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index f88a16e67..263dcb585 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -674,7 +674,7 @@ class HttpSocket extends CakeSocket { * Parses and sets the specified URI into current request configuration. * * @param mixed $uri URI, See HttpSocket::_parseUri() - * @return array Current configuration settings + * @return boolean If uri has merged in config */ protected function _configUri($uri = null) { if (empty($uri)) { @@ -697,7 +697,7 @@ class HttpSocket extends CakeSocket { ); $this->config = Set::merge($this->config, $config); $this->config = Set::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config)); - return $this->config; + return true; } /** diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index df4914ec3..d589f11a3 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -290,12 +290,12 @@ class HttpSocketTest extends CakeTestCase { ) ); $this->assertEquals($this->Socket->config, $expected); - $this->assertEquals($r, $expected); + $this->assertTrue($r); $r = $this->Socket->configUri(array('host' => 'www.foo-bar.org')); $expected['host'] = 'www.foo-bar.org'; $expected['request']['uri']['host'] = 'www.foo-bar.org'; $this->assertEquals($this->Socket->config, $expected); - $this->assertEquals($r, $expected); + $this->assertTrue($r); $r = $this->Socket->configUri('http://www.foo.com'); $expected = array( @@ -314,13 +314,13 @@ class HttpSocketTest extends CakeTestCase { ) ); $this->assertEquals($this->Socket->config, $expected); - $this->assertEquals($r, $expected); + $this->assertTrue($r); $r = $this->Socket->configUri('/this-is-broken'); $this->assertEquals($this->Socket->config, $expected); - $this->assertEquals($r, false); + $this->assertFalse($r); $r = $this->Socket->configUri(false); $this->assertEquals($this->Socket->config, $expected); - $this->assertEquals($r, false); + $this->assertFalse($r); } /** From d656bdae3b3c622f03a8eabab9dd8c09e194a71d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 6 Dec 2010 11:28:40 -0200 Subject: [PATCH 34/52] Renamed proxy method and calling togheter from host config. --- cake/libs/http_socket.php | 4 ++-- cake/tests/cases/libs/http_socket.test.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 263dcb585..84742fddd 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -245,6 +245,7 @@ class HttpSocket extends CakeSocket { if (isset($host)) { $this->config['host'] = $host; } + $this->_setProxy(); $cookies = null; if (is_array($this->request['header'])) { @@ -275,7 +276,6 @@ class HttpSocket extends CakeSocket { $this->setAuthConfig('Basic', $this->request['uri']['user'], $this->request['uri']['pass']); } $this->_setAuth(); - $this->_setProxyConfig(); if (is_array($this->request['body'])) { $this->request['body'] = $this->_httpSerialize($this->request['body']); @@ -501,7 +501,7 @@ class HttpSocket extends CakeSocket { * @return void * @throws Exception */ - protected function _setProxyConfig() { + protected function _setProxy() { if (empty($this->_proxy) || !isset($this->_proxy['host'], $this->_proxy['port'])) { return; } diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index d589f11a3..156dc9816 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -662,7 +662,7 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($this->Socket->config['port'], 123); $this->Socket->setAuthConfig('Test', 'login', 'passwd'); - $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nAuthorization: Test login.passwd\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; + $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\nAuthorization: Test login.passwd\r\n\r\n"; $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); } From 7c23d289e0945f0af53ef2e219ca5c4aa95c6aea Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 6 Dec 2010 12:03:22 -0200 Subject: [PATCH 35/52] Minor optimizations. --- cake/libs/http_socket.php | 2 +- cake/tests/cases/libs/http_socket.test.php | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 84742fddd..545762b75 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -321,7 +321,7 @@ class HttpSocket extends CakeSocket { $response .= $data; } - if ($connectionType == 'close') { + if ($connectionType === 'close') { $this->disconnect(); } diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 156dc9816..a7410231f 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -251,13 +251,12 @@ class HttpSocketTest extends CakeTestCase { $baseConfig['host'] = 'foo-bar'; $baseConfig['protocol'] = getprotobyname($baseConfig['protocol']); $this->assertEquals($this->Socket->config, $baseConfig); + $this->Socket->reset(); $baseConfig = $this->Socket->config; $this->Socket->__construct('http://www.cakephp.org:23/'); - $baseConfig['host'] = 'www.cakephp.org'; - $baseConfig['request']['uri']['host'] = 'www.cakephp.org'; - $baseConfig['port'] = 23; - $baseConfig['request']['uri']['port'] = 23; + $baseConfig['host'] = $baseConfig['request']['uri']['host'] = 'www.cakephp.org'; + $baseConfig['port'] = $baseConfig['request']['uri']['port'] = 23; $baseConfig['protocol'] = getprotobyname($baseConfig['protocol']); $this->assertEquals($this->Socket->config, $baseConfig); From bcacace061106e8c04b02c670992b1bf8c678d9a Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Mon, 6 Dec 2010 12:04:00 -0200 Subject: [PATCH 36/52] Updating docs. --- cake/libs/http_socket.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 545762b75..a128e96ad 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -41,7 +41,7 @@ class HttpSocket extends CakeSocket { public $quirksMode = false; /** - * The default values to use for a request + * Contain information about the last request (read only) * * @var array */ @@ -69,7 +69,7 @@ class HttpSocket extends CakeSocket { ); /** - * The default structure for storing the response + * Contain information about the last response (read only) * * @var array */ @@ -91,7 +91,7 @@ class HttpSocket extends CakeSocket { ); /** - * Default configuration settings for the HttpSocket + * Configuration settings for the HttpSocket and the requests * * @var array */ From 453c5364c28dc86841582912a72a50608f68f12c Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 10 Dec 2010 10:38:49 -0200 Subject: [PATCH 37/52] Putting the auth and proxy data in request attribute after request. --- cake/libs/http_socket.php | 3 ++ cake/tests/cases/libs/http_socket.test.php | 55 ++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index a128e96ad..53e010874 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -246,6 +246,8 @@ class HttpSocket extends CakeSocket { $this->config['host'] = $host; } $this->_setProxy(); + $this->request['proxy'] = $this->_proxy; + $cookies = null; if (is_array($this->request['header'])) { @@ -276,6 +278,7 @@ class HttpSocket extends CakeSocket { $this->setAuthConfig('Basic', $this->request['uri']['user'], $this->request['uri']['pass']); } $this->_setAuth(); + $this->request['auth'] = $this->_auth; if (is_array($this->request['body'])) { $this->request['body'] = $this->_httpSerialize($this->request['body']); diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index a7410231f..aac957c0d 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -349,7 +349,7 @@ class HttpSocketTest extends CakeTestCase { 'host' => 'www.cakephp.org', 'port' => 80 ), - 'cookies' => array() + 'cookies' => array(), ) ), 'request' => array( @@ -369,7 +369,9 @@ class HttpSocketTest extends CakeTestCase { 'line' => "GET /?foo=bar HTTP/1.1\r\n", 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n", 'raw' => "", - 'cookies' => array() + 'cookies' => array(), + 'proxy' => array(), + 'auth' => array() ) ) ), @@ -621,11 +623,11 @@ class HttpSocketTest extends CakeTestCase { } /** - * testRequestResultAsPointer method + * testRequestResultAsReference method * * @return void */ - public function testRequestResultAsPointer() { + public function testRequestResultAsReference() { $request = array('uri' => 'htpp://www.cakephp.org/'); $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a cookie test!

"; $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); @@ -652,6 +654,28 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($this->Socket->request['raw'], $expected); $this->assertEqual($this->Socket->config['host'], 'proxy.server'); $this->assertEqual($this->Socket->config['port'], 123); + $expected = array( + 'host' => 'proxy.server', + 'port' => 123, + 'method' => null, + 'user' => null, + 'pass' => null + ); + $this->assertEqual($this->Socket->request['proxy'], $expected); + + $expected = "GET http://www.cakephp.org/bakery HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n"; + $this->Socket->request('/bakery'); + $this->assertEqual($this->Socket->request['raw'], $expected); + $this->assertEqual($this->Socket->config['host'], 'proxy.server'); + $this->assertEqual($this->Socket->config['port'], 123); + $expected = array( + 'host' => 'proxy.server', + 'port' => 123, + 'method' => null, + 'user' => null, + 'pass' => null + ); + $this->assertEqual($this->Socket->request['proxy'], $expected); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; $this->Socket->setProxyConfig('proxy.server', 123, 'Test', 'mark', 'secret'); @@ -659,11 +683,34 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($this->Socket->request['raw'], $expected); $this->assertEqual($this->Socket->config['host'], 'proxy.server'); $this->assertEqual($this->Socket->config['port'], 123); + $expected = array( + 'host' => 'proxy.server', + 'port' => 123, + 'method' => 'Test', + 'user' => 'mark', + 'pass' => 'secret' + ); + $this->assertEqual($this->Socket->request['proxy'], $expected); $this->Socket->setAuthConfig('Test', 'login', 'passwd'); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\nAuthorization: Test login.passwd\r\n\r\n"; $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); + $expected = array( + 'host' => 'proxy.server', + 'port' => 123, + 'method' => 'Test', + 'user' => 'mark', + 'pass' => 'secret' + ); + $this->assertEqual($this->Socket->request['proxy'], $expected); + $expected = array( + 'Test' => array( + 'user' => 'login', + 'pass' => 'passwd' + ) + ); + $this->assertEqual($this->Socket->request['auth'], $expected); } /** From eeafb55d314f3a03508d7067b8d3c46bd3a7ea40 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Fri, 10 Dec 2010 12:08:49 -0200 Subject: [PATCH 38/52] Support to download requests. --- cake/libs/http_socket.php | 46 +++++++++++++++++++++- cake/tests/cases/libs/http_socket.test.php | 28 +++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 53e010874..0ecc1a60b 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -132,6 +132,13 @@ class HttpSocket extends CakeSocket { */ protected $_proxy = array(); +/** + * Resource to receive the content of request + * + * @var mixed + */ + protected $_contentResource = null; + /** * Build an HTTP Socket using the specified configuration. * @@ -208,6 +215,24 @@ class HttpSocket extends CakeSocket { $this->_proxy = compact('host', 'port', 'method', 'user', 'pass'); } +/** + * Set the resource to receive the request content. This resource must support fwrite. + * + * @param mixed $resource Resource or false to disable the resource use + * @return void + * @throw Exception + */ + public function setContentResource($resource) { + if ($resource === false) { + $this->_contentResource = null; + return; + } + if (!is_resource($resource)) { + throw new Exception(__('Invalid resource.')); + } + $this->_contentResource = $resource; + } + /** * Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this * method and provide a more granular interface. @@ -320,8 +345,27 @@ class HttpSocket extends CakeSocket { $this->write($this->request['raw']); $response = null; + $inHeader = true; while ($data = $this->read()) { - $response .= $data; + if ($this->_contentResource) { + if ($inHeader) { + $response .= $data; + $pos = strpos($response, "\r\n\r\n"); + if ($pos !== false) { + $pos += 4; + $data = substr($response, $pos); + fwrite($this->_contentResource, $data); + + $response = substr($response, 0, $pos); + $inHeader = false; + } + } else { + fwrite($this->_contentResource, $data); + fflush($this->_contentResource); + } + } else { + $response .= $data; + } } if ($connectionType === 'close') { diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index aac957c0d..32be35f7b 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -638,6 +638,34 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($data, $this->Socket->response['body']); } +/** + * testRequestWithResource + * + * @return void + */ + public function testRequestWithResource() { + $serverResponse = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->expects($this->at(2))->method('read')->will($this->returnValue(false)); + $this->Socket->expects($this->at(4))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->connected = true; + + $f = fopen(TMP . 'download.txt', 'w'); + $this->skipUnless($f, 'Can not write in TMP directory.'); + + $this->Socket->setContentResource($f); + $result = $this->Socket->request('http://www.cakephp.org/'); + $this->assertEqual($result, ''); + $this->assertEqual($this->Socket->response['header']['Server'], 'CakeHttp Server'); + fclose($f); + $this->assertEqual(file_get_contents(TMP . 'download.txt'), '

This is a test!

'); + unlink(TMP . 'download.txt'); + + $this->Socket->setContentResource(false); + $result = $this->Socket->request('http://www.cakephp.org/'); + $this->assertEqual($result, '

This is a test!

'); + } + /** * testProxy method * From d332f0624fbe91afe5524b55d4fa0826b239d710 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sat, 11 Dec 2010 16:49:19 -0200 Subject: [PATCH 39/52] Making the cookies independent for each host. --- cake/libs/http_socket.php | 19 ++++++++-- cake/tests/cases/libs/http_socket.test.php | 41 +++++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 0ecc1a60b..0af827f44 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -263,10 +263,21 @@ class HttpSocket extends CakeSocket { } $request['uri'] = $this->url($request['uri']); $request['uri'] = $this->_parseUri($request['uri'], true); - $this->request = Set::merge($this->request, $this->config['request'], $request); + $this->request = Set::merge($this->request, array_diff_key($this->config['request'], array('cookies' => true)), $request); $this->_configUri($this->request['uri']); + $Host = $this->request['uri']['host']; + if (!empty($this->config['request']['cookies'][$Host])) { + if (!isset($this->request['cookies'])) { + $this->request['cookies'] = array(); + } + if (!isset($request['cookies'])) { + $request['cookies'] = array(); + } + $this->request['cookies'] = array_merge($this->request['cookies'], $this->config['request']['cookies'][$Host], $request['cookies']); + } + if (isset($host)) { $this->config['host'] = $host; } @@ -280,7 +291,6 @@ class HttpSocket extends CakeSocket { if (!empty($this->request['cookies'])) { $cookies = $this->buildCookies($this->request['cookies']); } - $Host = $this->request['uri']['host']; $schema = ''; $port = 0; if (isset($this->request['uri']['schema'])) { @@ -374,7 +384,10 @@ class HttpSocket extends CakeSocket { $this->response = $this->_parseResponse($response); if (!empty($this->response['cookies'])) { - $this->config['request']['cookies'] = array_merge($this->config['request']['cookies'], $this->response['cookies']); + if (!isset($this->config['request']['cookies'][$Host])) { + $this->config['request']['cookies'][$Host] = array(); + } + $this->config['request']['cookies'][$Host] = array_merge($this->config['request']['cookies'][$Host], $this->response['cookies']); } return $this->response['body']; diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 32be35f7b..895b62e08 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -618,7 +618,7 @@ class HttpSocketTest extends CakeTestCase { ) ); $this->assertEqual($result, $expect); - $this->assertEqual($this->Socket->config['request']['cookies'], $expect); + $this->assertEqual($this->Socket->config['request']['cookies']['www.cakephp.org'], $expect); $this->assertFalse($this->Socket->connected); } @@ -666,6 +666,45 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($result, '

This is a test!

'); } +/** + * testRequestWithCrossCookie + * + * @return void + */ + public function testRequestWithCrossCookie() { + $this->Socket->connected = true; + $this->Socket->config['request']['cookies'] = array(); + + $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->expects($this->at(2))->method('read')->will($this->returnValue(false)); + $expected = array('www.cakephp.org' => array('foo' => array('value' => 'bar'))); + $this->Socket->request('http://www.cakephp.org/'); + $this->assertEqual($this->Socket->config['request']['cookies'], $expected); + + $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: bar=foo\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->expects($this->at(2))->method('read')->will($this->returnValue(false)); + $this->Socket->request('http://www.cakephp.org/other'); + $this->assertEqual($this->Socket->request['cookies'], array('foo' => array('value' => 'bar'))); + $expected['www.cakephp.org'] += array('bar' => array('value' => 'foo')); + $this->assertEqual($this->Socket->config['request']['cookies'], $expected); + + $serverResponse = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->expects($this->at(2))->method('read')->will($this->returnValue(false)); + $this->Socket->request('/other2'); + $this->assertEqual($this->Socket->config['request']['cookies'], $expected); + + $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foobar=ok\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->expects($this->at(2))->method('read')->will($this->returnValue(false)); + $this->Socket->request('http://www.cake.com'); + $this->assertTrue(empty($this->Socket->request['cookies'])); + $expected['www.cake.com'] = array('foobar' => array('value' => 'ok')); + $this->assertEqual($this->Socket->config['request']['cookies'], $expected); + } + /** * testProxy method * From c20b5d70538638273d79edf2ae14c9d2ebbcb581 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Sun, 12 Dec 2010 23:48:04 -0200 Subject: [PATCH 40/52] Renamed setAuthConfig to configAuth, and setProxyConfig to configProxy. --- cake/libs/http_socket.php | 6 +++--- cake/tests/cases/libs/http_socket.test.php | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 0af827f44..5c58745c6 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -181,7 +181,7 @@ class HttpSocket extends CakeSocket { * @param string $pass Password for authentication * @return void */ - public function setAuthConfig($method, $user = null, $pass = null) { + public function configAuth($method, $user = null, $pass = null) { if (empty($method)) { $this->_auth = array(); return; @@ -203,7 +203,7 @@ class HttpSocket extends CakeSocket { * @param string $pass Password to proxy authentication * @return void */ - public function setProxyConfig($host, $port = 3128, $method = null, $user = null, $pass = null) { + public function configProxy($host, $port = 3128, $method = null, $user = null, $pass = null) { if (empty($host)) { $this->_proxy = array(); return; @@ -310,7 +310,7 @@ class HttpSocket extends CakeSocket { } if (isset($this->request['uri']['user'], $this->request['uri']['pass'])) { - $this->setAuthConfig('Basic', $this->request['uri']['user'], $this->request['uri']['pass']); + $this->configAuth('Basic', $this->request['uri']['user'], $this->request['uri']['pass']); } $this->_setAuth(); $this->request['auth'] = $this->_auth; diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 895b62e08..73c44fe62 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -715,7 +715,7 @@ class HttpSocketTest extends CakeTestCase { $this->Socket->expects($this->any())->method('connect')->will($this->returnValue(true)); $this->Socket->expects($this->any())->method('read')->will($this->returnValue(false)); - $this->Socket->setProxyConfig('proxy.server', 123); + $this->Socket->configProxy('proxy.server', 123); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n"; $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); @@ -745,7 +745,7 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($this->Socket->request['proxy'], $expected); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\n\r\n"; - $this->Socket->setProxyConfig('proxy.server', 123, 'Test', 'mark', 'secret'); + $this->Socket->configProxy('proxy.server', 123, 'Test', 'mark', 'secret'); $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); $this->assertEqual($this->Socket->config['host'], 'proxy.server'); @@ -759,7 +759,7 @@ class HttpSocketTest extends CakeTestCase { ); $this->assertEqual($this->Socket->request['proxy'], $expected); - $this->Socket->setAuthConfig('Test', 'login', 'passwd'); + $this->Socket->configAuth('Test', 'login', 'passwd'); $expected = "GET http://www.cakephp.org/ HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nProxy-Authorization: Test mark.secret\r\nAuthorization: Test login.passwd\r\n\r\n"; $this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($this->Socket->request['raw'], $expected); @@ -879,11 +879,11 @@ class HttpSocketTest extends CakeTestCase { $socket->get('http://mark:secret@example.com/test'); $this->assertTrue(strpos($socket->request['header'], 'Authorization: Basic bWFyazpzZWNyZXQ=') !== false); - $socket->setAuthConfig(false); + $socket->configAuth(false); $socket->get('http://example.com/test'); $this->assertFalse(strpos($socket->request['header'], 'Authorization:')); - $socket->setAuthConfig('Test', 'mark', 'passwd'); + $socket->configAuth('Test', 'mark', 'passwd'); $socket->get('http://example.com/test'); $this->assertTrue(strpos($socket->request['header'], 'Authorization: Test mark.passwd') !== false); } From dfb76d67da6e14b4c3b01ebfe371518ca9a62ed1 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 00:16:45 -0200 Subject: [PATCH 41/52] Created the HttpResponse to get HttpSocket responses. --- cake/libs/http_response.php | 180 +++++++++++++++++++ cake/tests/cases/libs/all_socket.test.php | 1 + cake/tests/cases/libs/http_response.test.php | 157 ++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 cake/libs/http_response.php create mode 100644 cake/tests/cases/libs/http_response.test.php diff --git a/cake/libs/http_response.php b/cake/libs/http_response.php new file mode 100644 index 000000000..ef54423ab --- /dev/null +++ b/cake/libs/http_response.php @@ -0,0 +1,180 @@ +body; + } + +/** + * Get header in case insensitive + * + * @param string $name Header name + * @return mixed String if header exists or null + */ + public function getHeader($name) { + if (isset($this->headers[$name])) { + return $this->headers[$name]; + } + foreach ($this->headers as $key => $value) { + if (strcasecmp($key, $name) == 0) { + return $value; + } + } + return null; + } + +/** + * If return is 200 (OK) + * + * @return boolean + */ + public function isOk() { + return $this->code == 200; + } + +/** + * ArrayAccess - Offset Exists + * + * @param mixed $offset + * @return boolean + */ + public function offsetExists($offset) { + return in_array($offset, array('raw', 'status', 'header', 'body', 'cookies')); + } + +/** + * ArrayAccess - Offset Get + * + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) { + switch ($offset) { + case 'raw': + $firstLineLength = strpos($this->raw, "\r\n") + 2; + return array( + 'status-line' => $this->httpVersion . ' ' . $this->code . ' ' . $this->reasonPhrase, + 'header' => substr($this->raw, $firstLineLength, strpos($this->raw, "\r\n\r\n") - $firstLineLength), + 'body' => $this->body, + 'response' => $this->raw + ); + case 'status': + return array( + 'http-version' => $this->httpVersion, + 'code' => $this->code, + 'reason-phrase' => $this->reasonPhrase + ); + case 'header': + return $this->headers; + case 'body': + return $this->body; + case 'cookies': + return $this->cookies; + } + return null; + } + +/** + * ArrayAccess - 0ffset Set + * + * @param mixed $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset, $value) { + return; + } + +/** + * ArrayAccess - Offset Unset + * + * @param mixed @offset + * @return void + */ + public function offsetUnset($offset) { + return; + } + +/** + * Instance as string + * + * @return string + */ + public function __toString() { + return $this->body(); + } + +} diff --git a/cake/tests/cases/libs/all_socket.test.php b/cake/tests/cases/libs/all_socket.test.php index 896738957..ec276f580 100644 --- a/cake/tests/cases/libs/all_socket.test.php +++ b/cake/tests/cases/libs/all_socket.test.php @@ -38,6 +38,7 @@ class AllSocketTest extends PHPUnit_Framework_TestSuite { $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'cake_socket.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'http_socket.test.php'); + $suite->addTestFile(CAKE_TEST_CASES . DS . 'libs' . DS . 'http_response.test.php'); $suite->addTestDirectory(CORE_TEST_CASES . DS . 'libs' . DS . 'http'); return $suite; } diff --git a/cake/tests/cases/libs/http_response.test.php b/cake/tests/cases/libs/http_response.test.php new file mode 100644 index 000000000..debac0735 --- /dev/null +++ b/cake/tests/cases/libs/http_response.test.php @@ -0,0 +1,157 @@ + + * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.4206 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +App::import('Core', 'HttpResponse'); + +/** + * HttpResponseTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class HttpResponseTest extends CakeTestCase { +/** + * This function sets up a HttpResponse + * + * @return void + */ + public function setUp() { + $this->HttpResponse = new HttpResponse(); + } + +/** + * testBody + * + * @return void + */ + public function testBody() { + $this->HttpResponse->body = 'testing'; + $this->assertEqual($this->HttpResponse->body(), 'testing'); + + $this->HttpResponse->body = null; + $this->assertIdentical($this->HttpResponse->body(), ''); + } + +/** + * testToString + * + * @return void + */ + public function testToString() { + $this->HttpResponse->body = 'other test'; + $this->assertEqual($this->HttpResponse->body(), 'other test'); + $this->assertEqual((string)$this->HttpResponse, 'other test'); + $this->assertTrue(strpos($this->HttpResponse, 'test') > 0); + + $this->HttpResponse->body = null; + $this->assertEqual((string)$this->HttpResponse, ''); + } + +/** + * testGetHeadr + * + * @return void + */ + public function testGetHeader() { + $this->HttpResponse->headers = array( + 'foo' => 'Bar', + 'Some' => 'ok', + 'HeAdEr' => 'value', + 'content-Type' => 'text/plain' + ); + + $this->assertEqual($this->HttpResponse->getHeader('foo'), 'Bar'); + $this->assertEqual($this->HttpResponse->getHeader('Foo'), 'Bar'); + $this->assertEqual($this->HttpResponse->getHeader('FOO'), 'Bar'); + $this->assertEqual($this->HttpResponse->getHeader('header'), 'value'); + $this->assertEqual($this->HttpResponse->getHeader('Content-Type'), 'text/plain'); + $this->assertIdentical($this->HttpResponse->getHeader(0), null); + } + +/** + * testIsOk + * + * @return void + */ + public function testIsOk() { + $this->HttpResponse->code = 0; + $this->assertFalse($this->HttpResponse->isOk()); + $this->HttpResponse->code = -1; + $this->assertFalse($this->HttpResponse->isOk()); + $this->HttpResponse->code = 201; + $this->assertFalse($this->HttpResponse->isOk()); + $this->HttpResponse->code = 'what?'; + $this->assertFalse($this->HttpResponse->isOk()); + $this->HttpResponse->code = 200; + $this->assertTrue($this->HttpResponse->isOk()); + } + +/** + * testArrayAccess + * + * @return void + */ + public function testArrayAccess() { + $this->HttpResponse->httpVersion = 'HTTP/1.1'; + $this->HttpResponse->code = 200; + $this->HttpResponse->reasonPhrase = 'OK'; + $this->HttpResponse->headers = array( + 'Server' => 'CakePHP', + 'ContEnt-Type' => 'text/plain' + ); + $this->HttpResponse->cookies = array( + 'foo' => array('value' => 'bar'), + 'bar' => array('value' => 'foo') + ); + $this->HttpResponse->body = 'This is a test!'; + $this->HttpResponse->raw = "HTTP/1.1 200 OK\r\nServer: CakePHP\r\nContEnt-Type: text/plain\r\n\r\nThis is a test!"; + + $expected1 = 'HTTP/1.1 200 OK'; + $this->assertEqual($this->HttpResponse['raw']['status-line'], $expected1); + $expected2 = "Server: CakePHP\r\nContEnt-Type: text/plain"; + $this->assertEqual($this->HttpResponse['raw']['header'], $expected2); + $expected3 = 'This is a test!'; + $this->assertEqual($this->HttpResponse['raw']['body'], $expected3); + $expected = $expected1 . "\r\n" . $expected2 . "\r\n\r\n" . $expected3; + $this->assertEqual($this->HttpResponse['raw']['response'], $expected); + + $expected = 'HTTP/1.1'; + $this->assertEqual($this->HttpResponse['status']['http-version'], $expected); + $expected = 200; + $this->assertEqual($this->HttpResponse['status']['code'], $expected); + $expected = 'OK'; + $this->assertEqual($this->HttpResponse['status']['reason-phrase'], $expected); + + $expected = array( + 'Server' => 'CakePHP', + 'ContEnt-Type' => 'text/plain' + ); + $this->assertEqual($this->HttpResponse['header'], $expected); + + $expected = 'This is a test!'; + $this->assertEqual($this->HttpResponse['body'], $expected); + + $expected = array( + 'foo' => array('value' => 'bar'), + 'bar' => array('value' => 'foo') + ); + $this->assertEqual($this->HttpResponse['cookies'], $expected); + } + +} \ No newline at end of file From f45027ecd87041be3a8f257664b661650a315bd7 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 01:06:57 -0200 Subject: [PATCH 42/52] Adjusting HttpResponse responses in array to be more compatible. --- cake/libs/http_response.php | 9 +++++++-- cake/tests/cases/libs/http_response.test.php | 9 ++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cake/libs/http_response.php b/cake/libs/http_response.php index ef54423ab..f0cc637c8 100644 --- a/cake/libs/http_response.php +++ b/cake/libs/http_response.php @@ -125,9 +125,14 @@ class HttpResponse implements ArrayAccess { switch ($offset) { case 'raw': $firstLineLength = strpos($this->raw, "\r\n") + 2; + if ($this->raw[$firstLineLength] === "\r") { + $header = null; + } else { + $header = substr($this->raw, $firstLineLength, strpos($this->raw, "\r\n\r\n") - $firstLineLength) . "\r\n"; + } return array( - 'status-line' => $this->httpVersion . ' ' . $this->code . ' ' . $this->reasonPhrase, - 'header' => substr($this->raw, $firstLineLength, strpos($this->raw, "\r\n\r\n") - $firstLineLength), + 'status-line' => $this->httpVersion . ' ' . $this->code . ' ' . $this->reasonPhrase . "\r\n", + 'header' => $header, 'body' => $this->body, 'response' => $this->raw ); diff --git a/cake/tests/cases/libs/http_response.test.php b/cake/tests/cases/libs/http_response.test.php index debac0735..6c3967a89 100644 --- a/cake/tests/cases/libs/http_response.test.php +++ b/cake/tests/cases/libs/http_response.test.php @@ -122,13 +122,13 @@ class HttpResponseTest extends CakeTestCase { $this->HttpResponse->body = 'This is a test!'; $this->HttpResponse->raw = "HTTP/1.1 200 OK\r\nServer: CakePHP\r\nContEnt-Type: text/plain\r\n\r\nThis is a test!"; - $expected1 = 'HTTP/1.1 200 OK'; + $expected1 = "HTTP/1.1 200 OK\r\n"; $this->assertEqual($this->HttpResponse['raw']['status-line'], $expected1); - $expected2 = "Server: CakePHP\r\nContEnt-Type: text/plain"; + $expected2 = "Server: CakePHP\r\nContEnt-Type: text/plain\r\n"; $this->assertEqual($this->HttpResponse['raw']['header'], $expected2); $expected3 = 'This is a test!'; $this->assertEqual($this->HttpResponse['raw']['body'], $expected3); - $expected = $expected1 . "\r\n" . $expected2 . "\r\n\r\n" . $expected3; + $expected = $expected1 . $expected2 . "\r\n" . $expected3; $this->assertEqual($this->HttpResponse['raw']['response'], $expected); $expected = 'HTTP/1.1'; @@ -152,6 +152,9 @@ class HttpResponseTest extends CakeTestCase { 'bar' => array('value' => 'foo') ); $this->assertEqual($this->HttpResponse['cookies'], $expected); + + $this->HttpResponse->raw = "HTTP/1.1 200 OK\r\n\r\nThis is a test!"; + $this->assertIdentical($this->HttpResponse['raw']['header'], null); } } \ No newline at end of file From 60a9d34027d9ab1d822fb75bcefe2c87bac7e4cf Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 01:07:25 -0200 Subject: [PATCH 43/52] Updated the HttpSocket to use the new HttpResponse. --- cake/libs/http_socket.php | 99 +++++++------------ cake/tests/cases/libs/http_socket.test.php | 110 +++++++++------------ 2 files changed, 81 insertions(+), 128 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index 5c58745c6..aa4769319 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -19,6 +19,7 @@ */ App::import('Core', 'CakeSocket'); App::import('Core', 'Router'); +App::import('Lib', 'HttpResponse'); /** * Cake network socket connection class. @@ -73,22 +74,7 @@ class HttpSocket extends CakeSocket { * * @var array */ - public $response = array( - 'raw' => array( - 'status-line' => null, - 'header' => null, - 'body' => null, - 'response' => null - ), - 'status' => array( - 'http-version' => null, - 'code' => null, - 'reason-phrase' => null - ), - 'header' => array(), - 'body' => '', - 'cookies' => array() - ); + public $response = null; /** * Configuration settings for the HttpSocket and the requests @@ -238,16 +224,15 @@ class HttpSocket extends CakeSocket { * method and provide a more granular interface. * * @param mixed $request Either an URI string, or an array defining host/uri - * @return mixed null on error, reference to request body on success + * @return mixed false on error, HttpResponse on success */ - public function &request($request = array()) { + public function request($request = array()) { $this->reset(false); if (is_string($request)) { $request = array('uri' => $request); } elseif (!is_array($request)) { - $return = false; - return $return; + return false; } if (!isset($request['uri'])) { @@ -338,7 +323,7 @@ class HttpSocket extends CakeSocket { } if ($this->quirksMode === false && $this->request['line'] === false) { - return $this->response = false; + return false; } $this->request['raw'] = ''; @@ -383,14 +368,14 @@ class HttpSocket extends CakeSocket { } $this->response = $this->_parseResponse($response); - if (!empty($this->response['cookies'])) { + if (!empty($this->response->cookies)) { if (!isset($this->config['request']['cookies'][$Host])) { $this->config['request']['cookies'][$Host] = array(); } - $this->config['request']['cookies'][$Host] = array_merge($this->config['request']['cookies'][$Host], $this->response['cookies']); + $this->config['request']['cookies'][$Host] = array_merge($this->config['request']['cookies'][$Host], $this->response->cookies); } - return $this->response['body']; + return $this->response; } /** @@ -416,7 +401,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request, either false on failure or the response to the request. */ - public function &get($uri = null, $query = array(), $request = array()) { + public function get($uri = null, $query = array(), $request = array()) { if (!empty($query)) { $uri = $this->_parseUri($uri); if (isset($uri['query'])) { @@ -448,7 +433,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request, either false on failure or the response to the request. */ - public function &post($uri = null, $data = array(), $request = array()) { + public function post($uri = null, $data = array(), $request = array()) { $request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request); return $this->request($request); } @@ -461,7 +446,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request */ - public function &put($uri = null, $data = array(), $request = array()) { + public function put($uri = null, $data = array(), $request = array()) { $request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request); return $this->request($request); } @@ -474,7 +459,7 @@ class HttpSocket extends CakeSocket { * @param array $request An indexed array with indexes such as 'method' or uri * @return mixed Result of request */ - public function &delete($uri = null, $data = array(), $request = array()) { + public function delete($uri = null, $data = array(), $request = array()) { $request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request); return $this->request($request); } @@ -585,58 +570,40 @@ class HttpSocket extends CakeSocket { * Parses the given message and breaks it down in parts. * * @param string $message Message to parse - * @return array Parsed message (with indexed elements such as raw, status, header, body) + * @return object Parsed message as HttpResponse */ protected function _parseResponse($message) { - if (is_array($message)) { - return $message; - } elseif (!is_string($message)) { - return false; + if (!is_string($message)) { + throw new Exception(__('Invalid response.')); } - static $responseTemplate; - - if (empty($responseTemplate)) { - $classVars = get_class_vars(__CLASS__); - $responseTemplate = $classVars['response']; - } - - $response = $responseTemplate; - if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) { - return false; + throw new Exception(__('Invalid HTTP response.')); } - list($null, $response['raw']['status-line'], $response['raw']['header']) = $match; - $response['raw']['response'] = $message; - $response['raw']['body'] = substr($message, strlen($match[0])); + $response = new HttpResponse(); - if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $response['raw']['status-line'], $match)) { - $response['status']['http-version'] = $match[1]; - $response['status']['code'] = (int)$match[2]; - $response['status']['reason-phrase'] = $match[3]; + list(, $statusLine, $header) = $match; + $response->raw = $message; + $response->body = (string)substr($message, strlen($match[0])); + + if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $statusLine, $match)) { + $response->httpVersion = $match[1]; + $response->code = $match[2]; + $response->reasonPhrase = $match[3]; } - $response['header'] = $this->_parseHeader($response['raw']['header']); - $transferEncoding = null; - if (isset($response['header']['Transfer-Encoding'])) { - $transferEncoding = $response['header']['Transfer-Encoding']; - } - $decoded = $this->_decodeBody($response['raw']['body'], $transferEncoding); - $response['body'] = $decoded['body']; + $response->headers = $this->_parseHeader($header); + $transferEncoding = $response->getHeader('Transfer-Encoding'); + $decoded = $this->_decodeBody($response->body, $transferEncoding); + $response->body = $decoded['body']; if (!empty($decoded['header'])) { - $response['header'] = $this->_parseHeader($this->_buildHeader($response['header']) . $this->_buildHeader($decoded['header'])); + $response->headers = $this->_parseHeader($this->_buildHeader($response->headers) . $this->_buildHeader($decoded['header'])); } - if (!empty($response['header'])) { - $response['cookies'] = $this->parseCookies($response['header']); - } - - foreach ($response['raw'] as $field => $val) { - if ($val === '') { - $response['raw'][$field] = null; - } + if (!empty($response->headers)) { + $response->cookies = $this->parseCookies($response->headers); } return $response; diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 73c44fe62..9dff42b8f 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -190,6 +190,19 @@ class TestHttpSocket extends HttpSocket { public function unescapeToken($token, $chars = null) { return parent::_unescapeToken($token, $chars); } + +/** + * Convenience method for testing protected method + * + * @param string $message + * @return object HttpResponse + */ + protected function _parseResponse($message) { + if (!is_string($message)) { + return false; + } + return parent::_parseResponse($message); + } } /** @@ -596,7 +609,7 @@ class HttpSocketTest extends CakeTestCase { $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); $this->Socket->expects($this->once())->method('write') ->with("GET / HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n"); - $response = $this->Socket->request($request); + $response = (string)$this->Socket->request($request); $this->assertEquals($response, "

Hello, your lucky number is " . $number . "

"); } @@ -622,22 +635,6 @@ class HttpSocketTest extends CakeTestCase { $this->assertFalse($this->Socket->connected); } -/** - * testRequestResultAsReference method - * - * @return void - */ - public function testRequestResultAsReference() { - $request = array('uri' => 'htpp://www.cakephp.org/'); - $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a cookie test!

"; - $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); - $this->Socket->connected = true; - $data =& $this->Socket->request($request); - $this->assertEqual($data, $this->Socket->response['body']); - $data = 'new data'; - $this->assertEqual($data, $this->Socket->response['body']); - } - /** * testRequestWithResource * @@ -654,7 +651,7 @@ class HttpSocketTest extends CakeTestCase { $this->skipUnless($f, 'Can not write in TMP directory.'); $this->Socket->setContentResource($f); - $result = $this->Socket->request('http://www.cakephp.org/'); + $result = (string)$this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($result, ''); $this->assertEqual($this->Socket->response['header']['Server'], 'CakeHttp Server'); fclose($f); @@ -662,7 +659,7 @@ class HttpSocketTest extends CakeTestCase { unlink(TMP . 'download.txt'); $this->Socket->setContentResource(false); - $result = $this->Socket->request('http://www.cakephp.org/'); + $result = (string)$this->Socket->request('http://www.cakephp.org/'); $this->assertEqual($result, '

This is a test!

'); } @@ -987,15 +984,6 @@ class HttpSocketTest extends CakeTestCase { public function testParseResponse() { $this->Socket->reset(); - $r = $this->Socket->parseResponse(array('foo' => 'bar')); - $this->assertEquals($r, array('foo' => 'bar')); - - $r = $this->Socket->parseResponse(true); - $this->assertEquals($r, false); - - $r = $this->Socket->parseResponse("HTTP Foo\r\nBar: La"); - $this->assertEquals($r, false); - $tests = array( 'simple-request' => array( 'response' => array( @@ -1004,10 +992,10 @@ class HttpSocketTest extends CakeTestCase { 'body' => "

Hello World

\r\n

It's good to be html

" ), 'expectations' => array( - 'status.http-version' => 'HTTP/1.x', - 'status.code' => 200, - 'status.reason-phrase' => 'OK', - 'header' => $this->Socket->parseHeader("Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n"), + 'httpVersion' => 'HTTP/1.x', + 'code' => 200, + 'reasonPhrase' => 'OK', + 'headers' => array('Date' => 'Mon, 16 Apr 2007 04:14:16 GMT', 'Server' => 'CakeHttp Server'), 'body' => "

Hello World

\r\n

It's good to be html

" ) ), @@ -1017,34 +1005,8 @@ class HttpSocketTest extends CakeTestCase { 'header' => null ), 'expectations' => array( - 'status.code' => 404, - 'header' => array() - ) - ), - 'chunked' => array( - 'response' => array( - 'header' => "Transfer-Encoding: chunked\r\n", - 'body' => "19\r\nThis is a chunked message\r\n0\r\n" - ), - 'expectations' => array( - 'body' => "This is a chunked message", - 'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\n") - ) - ), - 'enitity-header' => array( - 'response' => array( - 'body' => "19\r\nThis is a chunked message\r\n0\r\nFoo: Bar\r\n" - ), - 'expectations' => array( - 'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\nFoo: Bar\r\n") - ) - ), - 'enitity-header-combine' => array( - 'response' => array( - 'header' => "Transfer-Encoding: chunked\r\nFoo: Foobar\r\n" - ), - 'expectations' => array( - 'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\nFoo: Foobar\r\nFoo: Bar\r\n") + 'code' => 404, + 'headers' => array() ) ) ); @@ -1059,8 +1021,7 @@ class HttpSocketTest extends CakeTestCase { $expectations = array_merge($expectations, $test['expectations']); foreach ($expectations as $property => $expectedVal) { - $val = Set::extract($r, $property); - $this->assertEquals($val, $expectedVal, 'Test "' . $name . '": response.' . $property . ' - %s'); + $this->assertEquals($r->{$property}, $expectedVal, 'Test "' . $name . '": response.' . $property . ' - %s'); } foreach (array('status-line', 'header', 'body', 'response') as $field) { @@ -1069,6 +1030,31 @@ class HttpSocketTest extends CakeTestCase { } } +/** + * data provider function for testInvalidParseResponseData + * + * @return array + */ + public static function invalidParseResponseDataProvider() { + return array( + array(array('foo' => 'bar')), + array(true), + array("HTTP Foo\r\nBar: La"), + array('HTTP/1.1 TEST ERROR') + ); + } + +/** + * testInvalidParseResponseData + * + * @dataProvider invalidParseResponseDataProvider + * @expectedException Exception + * return void + */ + public function testInvalidParseResponseData($value) { + $this->Socket->parseResponse($value); + } + /** * testDecodeBody method * From 176da154172c9d7670086ad4dc25733e03912bec Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 02:22:30 -0200 Subject: [PATCH 44/52] Moved the response methods from HttpSocket to HttpResponse. --- cake/libs/http_response.php | 249 +++++++++++++++++ cake/libs/http_socket.php | 176 +----------- cake/tests/cases/libs/http_response.test.php | 236 +++++++++++++++- cake/tests/cases/libs/http_socket.test.php | 272 ------------------- 4 files changed, 486 insertions(+), 447 deletions(-) diff --git a/cake/libs/http_response.php b/cake/libs/http_response.php index f0cc637c8..aea0133ae 100644 --- a/cake/libs/http_response.php +++ b/cake/libs/http_response.php @@ -69,6 +69,16 @@ class HttpResponse implements ArrayAccess { */ public $raw = ''; +/** + * Contructor + * + */ + public function __construct($message = null) { + if ($message !== null) { + $this->parseResponse($message); + } + } + /** * Body content * @@ -105,6 +115,245 @@ class HttpResponse implements ArrayAccess { return $this->code == 200; } +/** + * Parses the given message and breaks it down in parts. + * + * @param string $message Message to parse + * @return void + * @throw Exception + */ + public function parseResponse($message) { + if (!is_string($message)) { + throw new Exception(__('Invalid response.')); + } + + if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) { + throw new Exception(__('Invalid HTTP response.')); + } + + list(, $statusLine, $header) = $match; + $this->raw = $message; + $this->body = (string)substr($message, strlen($match[0])); + + if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $statusLine, $match)) { + $this->httpVersion = $match[1]; + $this->code = $match[2]; + $this->reasonPhrase = $match[3]; + } + + $this->headers = $this->_parseHeader($header); + $transferEncoding = $this->getHeader('Transfer-Encoding'); + $decoded = $this->_decodeBody($this->body, $transferEncoding); + $this->body = $decoded['body']; + + if (!empty($decoded['header'])) { + $this->headers = $this->_parseHeader($this->_buildHeader($this->headers) . $this->_buildHeader($decoded['header'])); + } + + if (!empty($this->headers)) { + $this->cookies = $this->parseCookies($this->headers); + } + } + +/** + * Generic function to decode a $body with a given $encoding. Returns either an array with the keys + * 'body' and 'header' or false on failure. + * + * @param string $body A string continaing the body to decode. + * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding. + * @return mixed Array of response headers and body or false. + */ + protected function _decodeBody($body, $encoding = 'chunked') { + if (!is_string($body)) { + return false; + } + if (empty($encoding)) { + return array('body' => $body, 'header' => false); + } + $decodeMethod = '_decode' . Inflector::camelize(str_replace('-', '_', $encoding)) . 'Body'; + + if (!is_callable(array(&$this, $decodeMethod))) { + return array('body' => $body, 'header' => false); + } + return $this->{$decodeMethod}($body); + } + +/** + * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as + * a result. + * + * @param string $body A string continaing the chunked body to decode. + * @return mixed Array of response headers and body or false. + * @throws Exception + */ + protected function _decodeChunkedBody($body) { + if (!is_string($body)) { + return false; + } + + $decodedBody = null; + $chunkLength = null; + + while ($chunkLength !== 0) { + if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) { + throw new Exception(__('HttpSocket::_decodeChunkedBody - Could not parse malformed chunk.')); + } + + $chunkSize = 0; + $hexLength = 0; + $chunkExtensionName = ''; + $chunkExtensionValue = ''; + if (isset($match[0])) { + $chunkSize = $match[0]; + } + if (isset($match[1])) { + $hexLength = $match[1]; + } + if (isset($match[2])) { + $chunkExtensionName = $match[2]; + } + if (isset($match[3])) { + $chunkExtensionValue = $match[3]; + } + + $body = substr($body, strlen($chunkSize)); + $chunkLength = hexdec($hexLength); + $chunk = substr($body, 0, $chunkLength); + if (!empty($chunkExtensionName)) { + /** + * @todo See if there are popular chunk extensions we should implement + */ + } + $decodedBody .= $chunk; + if ($chunkLength !== 0) { + $body = substr($body, $chunkLength + strlen("\r\n")); + } + } + + $entityHeader = false; + if (!empty($body)) { + $entityHeader = $this->_parseHeader($body); + } + return array('body' => $decodedBody, 'header' => $entityHeader); + } + +/** + * Parses an array based header. + * + * @param array $header Header as an indexed array (field => value) + * @return array Parsed header + */ + protected function _parseHeader($header) { + if (is_array($header)) { + return $header; + } elseif (!is_string($header)) { + return false; + } + + preg_match_all("/(.+):(.+)(?:(?_unescapeToken($field); + + if (!isset($header[$field])) { + $header[$field] = $value; + } else { + $header[$field] = array_merge((array)$header[$field], (array)$value); + } + } + return $header; + } + +/** + * Parses cookies in response headers. + * + * @param array $header Header array containing one ore more 'Set-Cookie' headers. + * @return mixed Either false on no cookies, or an array of cookies recieved. + * @todo Make this 100% RFC 2965 confirm + */ + public function parseCookies($header) { + if (!isset($header['Set-Cookie'])) { + return false; + } + + $cookies = array(); + foreach ((array)$header['Set-Cookie'] as $cookie) { + if (strpos($cookie, '";"') !== false) { + $cookie = str_replace('";"', "{__cookie_replace__}", $cookie); + $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie)); + } else { + $parts = preg_split('/\;[ \t]*/', $cookie); + } + + list($name, $value) = explode('=', array_shift($parts), 2); + $cookies[$name] = compact('value'); + + foreach ($parts as $part) { + if (strpos($part, '=') !== false) { + list($key, $value) = explode('=', $part); + } else { + $key = $part; + $value = true; + } + + $key = strtolower($key); + if (!isset($cookies[$name][$key])) { + $cookies[$name][$key] = $value; + } + } + } + return $cookies; + } + +/** + * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs) + * + * @param string $token Token to unescape + * @param array $chars + * @return string Unescaped token + * @todo Test $chars parameter + */ + protected function _unescapeToken($token, $chars = null) { + $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/'; + $token = preg_replace($regex, '\\1', $token); + return $token; + } + +/** + * Gets escape chars according to RFC 2616 (HTTP 1.1 specs). + * + * @param boolean $hex true to get them as HEX values, false otherwise + * @param array $chars + * @return array Escape chars + * @todo Test $chars parameter + */ + protected function _tokenEscapeChars($hex = true, $chars = null) { + if (!empty($chars)) { + $escape = $chars; + } else { + $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " "); + for ($i = 0; $i <= 31; $i++) { + $escape[] = chr($i); + } + $escape[] = chr(127); + } + + if ($hex == false) { + return $escape; + } + $regexChars = ''; + foreach ($escape as $key => $char) { + $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT); + } + return $escape; + } + /** * ArrayAccess - Offset Exists * diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index aa4769319..c226b574f 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -367,7 +367,7 @@ class HttpSocket extends CakeSocket { $this->disconnect(); } - $this->response = $this->_parseResponse($response); + $this->response = new HttpResponse($response); if (!empty($this->response->cookies)) { if (!isset($this->config['request']['cookies'][$Host])) { $this->config['request']['cookies'][$Host] = array(); @@ -566,137 +566,6 @@ class HttpSocket extends CakeSocket { call_user_func("$authClass::proxyAuthentication", $this, &$this->_proxy); } -/** - * Parses the given message and breaks it down in parts. - * - * @param string $message Message to parse - * @return object Parsed message as HttpResponse - */ - protected function _parseResponse($message) { - if (!is_string($message)) { - throw new Exception(__('Invalid response.')); - } - - if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) { - throw new Exception(__('Invalid HTTP response.')); - } - - $response = new HttpResponse(); - - list(, $statusLine, $header) = $match; - $response->raw = $message; - $response->body = (string)substr($message, strlen($match[0])); - - if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $statusLine, $match)) { - $response->httpVersion = $match[1]; - $response->code = $match[2]; - $response->reasonPhrase = $match[3]; - } - - $response->headers = $this->_parseHeader($header); - $transferEncoding = $response->getHeader('Transfer-Encoding'); - $decoded = $this->_decodeBody($response->body, $transferEncoding); - $response->body = $decoded['body']; - - if (!empty($decoded['header'])) { - $response->headers = $this->_parseHeader($this->_buildHeader($response->headers) . $this->_buildHeader($decoded['header'])); - } - - if (!empty($response->headers)) { - $response->cookies = $this->parseCookies($response->headers); - } - - return $response; - } - -/** - * Generic function to decode a $body with a given $encoding. Returns either an array with the keys - * 'body' and 'header' or false on failure. - * - * @param string $body A string continaing the body to decode. - * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding. - * @return mixed Array of response headers and body or false. - */ - protected function _decodeBody($body, $encoding = 'chunked') { - if (!is_string($body)) { - return false; - } - if (empty($encoding)) { - return array('body' => $body, 'header' => false); - } - $decodeMethod = '_decode'.Inflector::camelize(str_replace('-', '_', $encoding)) . 'Body'; - - if (!is_callable(array(&$this, $decodeMethod))) { - if (!$this->quirksMode) { - trigger_error(sprintf(__('HttpSocket::_decodeBody - Unknown encoding: %s. Activate quirks mode to surpress error.'), h($encoding)), E_USER_WARNING); - } - return array('body' => $body, 'header' => false); - } - return $this->{$decodeMethod}($body); - } - -/** - * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as - * a result. - * - * @param string $body A string continaing the chunked body to decode. - * @return mixed Array of response headers and body or false. - * @throws Exception - */ - protected function _decodeChunkedBody($body) { - if (!is_string($body)) { - return false; - } - - $decodedBody = null; - $chunkLength = null; - - while ($chunkLength !== 0) { - if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) { - if (!$this->quirksMode) { - throw new Exception(__('HttpSocket::_decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.')); - } - break; - } - - $chunkSize = 0; - $hexLength = 0; - $chunkExtensionName = ''; - $chunkExtensionValue = ''; - if (isset($match[0])) { - $chunkSize = $match[0]; - } - if (isset($match[1])) { - $hexLength = $match[1]; - } - if (isset($match[2])) { - $chunkExtensionName = $match[2]; - } - if (isset($match[3])) { - $chunkExtensionValue = $match[3]; - } - - $body = substr($body, strlen($chunkSize)); - $chunkLength = hexdec($hexLength); - $chunk = substr($body, 0, $chunkLength); - if (!empty($chunkExtensionName)) { - /** - * @todo See if there are popular chunk extensions we should implement - */ - } - $decodedBody .= $chunk; - if ($chunkLength !== 0) { - $body = substr($body, $chunkLength + strlen("\r\n")); - } - } - - $entityHeader = false; - if (!empty($body)) { - $entityHeader = $this->_parseHeader($body); - } - return array('body' => $decodedBody, 'header' => $entityHeader); - } - /** * Parses and sets the specified URI into current request configuration. * @@ -1002,7 +871,7 @@ class HttpSocket extends CakeSocket { return false; } - preg_match_all("/(.+):(.+)(?:(?lineBreak . "|\$)/Uis", $header, $matches, PREG_SET_ORDER); + preg_match_all("/(.+):(.+)(?:(?HttpResponse = new HttpResponse(); + $this->HttpResponse = new TestHttpResponse(); } /** @@ -102,6 +133,209 @@ class HttpResponseTest extends CakeTestCase { $this->assertTrue($this->HttpResponse->isOk()); } +/** + * testParseResponse method + * + * @return void + */ + public function testParseResponse() { + $tests = array( + 'simple-request' => array( + 'response' => array( + 'status-line' => "HTTP/1.x 200 OK\r\n", + 'header' => "Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n", + 'body' => "

Hello World

\r\n

It's good to be html

" + ), + 'expectations' => array( + 'httpVersion' => 'HTTP/1.x', + 'code' => 200, + 'reasonPhrase' => 'OK', + 'headers' => array('Date' => 'Mon, 16 Apr 2007 04:14:16 GMT', 'Server' => 'CakeHttp Server'), + 'body' => "

Hello World

\r\n

It's good to be html

" + ) + ), + 'no-header' => array( + 'response' => array( + 'status-line' => "HTTP/1.x 404 OK\r\n", + 'header' => null + ), + 'expectations' => array( + 'code' => 404, + 'headers' => array() + ) + ) + ); + + $testResponse = array(); + $expectations = array(); + + foreach ($tests as $name => $test) { + $testResponse = array_merge($testResponse, $test['response']); + $testResponse['response'] = $testResponse['status-line'] . $testResponse['header'] . "\r\n" . $testResponse['body']; + $this->HttpResponse->parseResponse($testResponse['response']); + $expectations = array_merge($expectations, $test['expectations']); + + foreach ($expectations as $property => $expectedVal) { + $this->assertEquals($this->HttpResponse->{$property}, $expectedVal, 'Test "' . $name . '": response.' . $property . ' - %s'); + } + + foreach (array('status-line', 'header', 'body', 'response') as $field) { + $this->assertEquals($this->HttpResponse['raw'][$field], $testResponse[$field], 'Test response.raw.' . $field . ': %s'); + } + } + } + +/** + * data provider function for testInvalidParseResponseData + * + * @return array + */ + public static function invalidParseResponseDataProvider() { + return array( + array(array('foo' => 'bar')), + array(true), + array("HTTP Foo\r\nBar: La"), + array('HTTP/1.1 TEST ERROR') + ); + } + +/** + * testInvalidParseResponseData + * + * @dataProvider invalidParseResponseDataProvider + * @expectedException Exception + * return void + */ + public function testInvalidParseResponseData($value) { + $this->HttpResponse->parseResponse($value); + } + +/** + * testDecodeBody method + * + * @return void + */ + public function testDecodeBody() { + $r = $this->HttpResponse->decodeBody(true); + $this->assertEquals($r, false); + + $r = $this->HttpResponse->decodeBody('Foobar', false); + $this->assertEquals($r, array('body' => 'Foobar', 'header' => false)); + + $encoding = 'chunked'; + $sample = array( + 'encoded' => "19\r\nThis is a chunked message\r\n0\r\n", + 'decoded' => array('body' => "This is a chunked message", 'header' => false) + ); + + $r = $this->HttpResponse->decodeBody($sample['encoded'], $encoding); + $this->assertEquals($r, $sample['decoded']); + } + +/** + * testDecodeFooCoded + * + * @return void + */ + public function testDecodeFooCoded() { + $r = $this->HttpResponse->decodeBody(true); + $this->assertEquals($r, false); + + $r = $this->HttpResponse->decodeBody('Foobar', false); + $this->assertEquals($r, array('body' => 'Foobar', 'header' => false)); + + $encoding = 'foo-bar'; + $sample = array( + 'encoded' => '!Foobar!', + 'decoded' => array('body' => '!Foobar!', 'header' => false), + ); + + $r = $this->HttpResponse->decodeBody($sample['encoded'], $encoding); + $this->assertEquals($r, $sample['decoded']); + } + +/** + * testDecodeChunkedBody method + * + * @return void + */ + public function testDecodeChunkedBody() { + $r = $this->HttpResponse->decodeChunkedBody(true); + $this->assertEquals($r, false); + + $encoded = "19\r\nThis is a chunked message\r\n0\r\n"; + $decoded = "This is a chunked message"; + $r = $this->HttpResponse->decodeChunkedBody($encoded); + $this->assertEquals($r['body'], $decoded); + $this->assertEquals($r['header'], false); + + $encoded = "19 \r\nThis is a chunked message\r\n0\r\n"; + $r = $this->HttpResponse->decodeChunkedBody($encoded); + $this->assertEquals($r['body'], $decoded); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\n"; + $decoded = "This is a chunked message\nThat is cool\n"; + $r = $this->HttpResponse->decodeChunkedBody($encoded); + $this->assertEquals($r['body'], $decoded); + $this->assertEquals($r['header'], false); + + $encoded = "19\r\nThis is a chunked message\r\nE;foo-chunk=5\r\n\nThat is cool\n\r\n0\r\n"; + $r = $this->HttpResponse->decodeChunkedBody($encoded); + $this->assertEquals($r['body'], $decoded); + $this->assertEquals($r['header'], false); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; + $r = $this->HttpResponse->decodeChunkedBody($encoded); + $this->assertEquals($r['body'], $decoded); + $this->assertEquals($r['header'], array('foo-header' => 'bar', 'cake' => 'PHP')); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n"; + $this->expectError(); + $r = $this->HttpResponse->decodeChunkedBody($encoded); + $this->assertEquals($r, false); + } + +/** + * testParseCookies method + * + * @return void + */ + public function testParseCookies() { + $header = array( + 'Set-Cookie' => array( + 'foo=bar', + 'people=jim,jack,johnny";";Path=/accounts', + 'google=not=nice' + ), + 'Transfer-Encoding' => 'chunked', + 'Date' => 'Sun, 18 Nov 2007 18:57:42 GMT', + ); + $cookies = $this->HttpResponse->parseCookies($header); + $expected = array( + 'foo' => array( + 'value' => 'bar' + ), + 'people' => array( + 'value' => 'jim,jack,johnny";"', + 'path' => '/accounts', + ), + 'google' => array( + 'value' => 'not=nice', + ) + ); + $this->assertEqual($cookies, $expected); + + $header['Set-Cookie'][] = 'cakephp=great; Secure'; + $expected['cakephp'] = array('value' => 'great', 'secure' => true); + $cookies = $this->HttpResponse->parseCookies($header); + $this->assertEqual($cookies, $expected); + + $header['Set-Cookie'] = 'foo=bar'; + unset($expected['people'], $expected['cakephp'], $expected['google']); + $cookies = $this->HttpResponse->parseCookies($header); + $this->assertEqual($cookies, $expected); + } + /** * testArrayAccess * diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 9dff42b8f..b6a540759 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -99,16 +99,6 @@ class TestHttpSocket extends HttpSocket { return parent::_buildHeader($header, $mode); } -/** - * Convenience method for testing protected method - * - * @param string $message Message to parse - * @return array Parsed message (with indexed elements such as raw, status, header, body) - */ - public function parseResponse($message) { - return parent::_parseResponse($message); - } - /** * Convenience method for testing protected method * @@ -129,27 +119,6 @@ class TestHttpSocket extends HttpSocket { return parent::_parseQuery($query); } -/** - * Convenience method for testing protected method - * - * @param string $body A string continaing the body to decode - * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding - * @return mixed Array or false - */ - public function decodeBody($body, $encoding = 'chunked') { - return parent::_decodeBody($body, $encoding); - } - -/** - * Convenience method for testing protected method - * - * @param string $body A string continaing the chunked body to decode - * @return mixed Array or false - */ - public function decodeChunkedBody($body) { - return parent::_decodeChunkedBody($body); - } - /** * Convenience method for testing protected method * @@ -191,18 +160,6 @@ class TestHttpSocket extends HttpSocket { return parent::_unescapeToken($token, $chars); } -/** - * Convenience method for testing protected method - * - * @param string $message - * @return object HttpResponse - */ - protected function _parseResponse($message) { - if (!is_string($message)) { - return false; - } - return parent::_parseResponse($message); - } } /** @@ -976,194 +933,6 @@ class HttpSocketTest extends CakeTestCase { $this->RequestSocket->delete('http://www.google.com/', null, array('line' => 'Hey Server')); } -/** - * testParseResponse method - * - * @return void - */ - public function testParseResponse() { - $this->Socket->reset(); - - $tests = array( - 'simple-request' => array( - 'response' => array( - 'status-line' => "HTTP/1.x 200 OK\r\n", - 'header' => "Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n", - 'body' => "

Hello World

\r\n

It's good to be html

" - ), - 'expectations' => array( - 'httpVersion' => 'HTTP/1.x', - 'code' => 200, - 'reasonPhrase' => 'OK', - 'headers' => array('Date' => 'Mon, 16 Apr 2007 04:14:16 GMT', 'Server' => 'CakeHttp Server'), - 'body' => "

Hello World

\r\n

It's good to be html

" - ) - ), - 'no-header' => array( - 'response' => array( - 'status-line' => "HTTP/1.x 404 OK\r\n", - 'header' => null - ), - 'expectations' => array( - 'code' => 404, - 'headers' => array() - ) - ) - ); - - $testResponse = array(); - $expectations = array(); - - foreach ($tests as $name => $test) { - $testResponse = array_merge($testResponse, $test['response']); - $testResponse['response'] = $testResponse['status-line'] . $testResponse['header'] . "\r\n" . $testResponse['body']; - $r = $this->Socket->parseResponse($testResponse['response']); - $expectations = array_merge($expectations, $test['expectations']); - - foreach ($expectations as $property => $expectedVal) { - $this->assertEquals($r->{$property}, $expectedVal, 'Test "' . $name . '": response.' . $property . ' - %s'); - } - - foreach (array('status-line', 'header', 'body', 'response') as $field) { - $this->assertEquals($r['raw'][$field], $testResponse[$field], 'Test response.raw.' . $field . ': %s'); - } - } - } - -/** - * data provider function for testInvalidParseResponseData - * - * @return array - */ - public static function invalidParseResponseDataProvider() { - return array( - array(array('foo' => 'bar')), - array(true), - array("HTTP Foo\r\nBar: La"), - array('HTTP/1.1 TEST ERROR') - ); - } - -/** - * testInvalidParseResponseData - * - * @dataProvider invalidParseResponseDataProvider - * @expectedException Exception - * return void - */ - public function testInvalidParseResponseData($value) { - $this->Socket->parseResponse($value); - } - -/** - * testDecodeBody method - * - * @return void - */ - public function testDecodeBody() { - $this->Socket->reset(); - - $r = $this->Socket->decodeBody(true); - $this->assertEquals($r, false); - - $r = $this->Socket->decodeBody('Foobar', false); - $this->assertEquals($r, array('body' => 'Foobar', 'header' => false)); - - $encoding = 'chunked'; - $sample = array( - 'encoded' => "19\r\nThis is a chunked message\r\n0\r\n", - 'decoded' => array('body' => "This is a chunked message", 'header' => false) - ); - - $r = $this->Socket->decodeBody($sample['encoded'], $encoding); - $this->assertEquals($r, $sample['decoded']); - } - -/** - * testDecodeFooCoded - * - * @return void - */ - public function testDecodeFooCoded() { - $this->Socket->reset(); - - $r = $this->Socket->decodeBody(true); - $this->assertEquals($r, false); - - $r = $this->Socket->decodeBody('Foobar', false); - $this->assertEquals($r, array('body' => 'Foobar', 'header' => false)); - - $encoding = 'foo-bar'; - $sample = array( - 'encoded' => '!Foobar!', - 'decoded' => array('body' => '!Foobar!', 'header' => false), - ); - - $this->Socket->quirksMode = true; - $r = $this->Socket->decodeBody($sample['encoded'], $encoding); - $this->assertEquals($r, $sample['decoded']); - $this->Socket->quirksMode = false; - - $this->expectError(); - $r = $this->Socket->decodeBody($sample['encoded'], $encoding); - $this->assertEquals($r, $sample['decoded']); - } - -/** - * testDecodeChunkedBody method - * - * @return void - */ - public function testDecodeChunkedBody() { - $this->Socket->reset(); - - $r = $this->Socket->decodeChunkedBody(true); - $this->assertEquals($r, false); - - $encoded = "19\r\nThis is a chunked message\r\n0\r\n"; - $decoded = "This is a chunked message"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], false); - - $encoded = "19 \r\nThis is a chunked message\r\n0\r\n"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - - $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\n"; - $decoded = "This is a chunked message\nThat is cool\n"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], false); - - $encoded = "19\r\nThis is a chunked message\r\nE;foo-chunk=5\r\n\nThat is cool\n\r\n0\r\n"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], false); - - $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], array('foo-header' => 'bar', 'cake' => 'PHP')); - - $this->Socket->quirksMode = true; - $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], array('foo-header' => 'bar', 'cake' => 'PHP')); - - $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n"; - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r['body'], $decoded); - $this->assertEquals($r['header'], false); - - $this->Socket->quirksMode = false; - $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n"; - $this->expectError(); - $r = $this->Socket->decodeChunkedBody($encoded); - $this->assertEquals($r, false); - } - /** * testBuildRequestLine method * @@ -1627,47 +1396,6 @@ class HttpSocketTest extends CakeTestCase { $this->assertEquals($r, $expected); } -/** - * testParseCookies method - * - * @return void - */ - public function testParseCookies() { - $header = array( - 'Set-Cookie' => array( - 'foo=bar', - 'people=jim,jack,johnny";";Path=/accounts', - 'google=not=nice' - ), - 'Transfer-Encoding' => 'chunked', - 'Date' => 'Sun, 18 Nov 2007 18:57:42 GMT', - ); - $cookies = $this->Socket->parseCookies($header); - $expected = array( - 'foo' => array( - 'value' => 'bar' - ), - 'people' => array( - 'value' => 'jim,jack,johnny";"', - 'path' => '/accounts', - ), - 'google' => array( - 'value' => 'not=nice', - ) - ); - $this->assertEqual($cookies, $expected); - - $header['Set-Cookie'][] = 'cakephp=great; Secure'; - $expected['cakephp'] = array('value' => 'great', 'secure' => true); - $cookies = $this->Socket->parseCookies($header); - $this->assertEqual($cookies, $expected); - - $header['Set-Cookie'] = 'foo=bar'; - unset($expected['people'], $expected['cakephp'], $expected['google']); - $cookies = $this->Socket->parseCookies($header); - $this->assertEqual($cookies, $expected); - } - /** * testBuildCookies method * From 2f64afe44e888a897ad7ea12f6b54f780af7b776 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 02:46:31 -0200 Subject: [PATCH 45/52] Allowing the user change the response class. --- cake/libs/http_socket.php | 13 ++++++- cake/tests/cases/libs/http_socket.test.php | 40 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index c226b574f..a4fea03f3 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -76,6 +76,13 @@ class HttpSocket extends CakeSocket { */ public $response = null; +/** + * Response classname + * + * @var string + */ + public $responseClass = 'HttpResponse'; + /** * Configuration settings for the HttpSocket and the requests * @@ -367,7 +374,11 @@ class HttpSocket extends CakeSocket { $this->disconnect(); } - $this->response = new HttpResponse($response); + if (!App::import('Lib', $this->responseClass)) { + throw new Exception(__('Class %s not found.', $this->responseClass)); + } + $responseClass = $this->responseClass; + $this->response = new $responseClass($response); if (!empty($this->response->cookies)) { if (!isset($this->config['request']['cookies'][$Host])) { $this->config['request']['cookies'][$Host] = array(); diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index b6a540759..45f0350c6 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -51,6 +51,29 @@ class TestAuthentication { } +/** + * CustomResponse + * + */ +class CustomResponse { + +/** + * First 10 chars + * + * @var string + */ + public $first10; + +/** + * Constructor + * + */ + public function __construct($message) { + $this->first10 = substr($message, 0, 10); + } + +} + /** * TestHttpSocket * @@ -659,6 +682,23 @@ class HttpSocketTest extends CakeTestCase { $this->assertEqual($this->Socket->config['request']['cookies'], $expected); } +/** + * testRequestCustomResponse + * + * @return void + */ + public function testRequestCustomResponse() { + $this->Socket->connected = true; + $serverResponse = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

This is a test!

"; + $this->Socket->expects($this->at(1))->method('read')->will($this->returnValue($serverResponse)); + $this->Socket->expects($this->at(2))->method('read')->will($this->returnValue(false)); + + $this->Socket->responseClass = 'CustomResponse'; + $response = $this->Socket->request('http://www.cakephp.org/'); + $this->assertIsA($response, 'CustomResponse'); + $this->assertEqual($response->first10, 'HTTP/1.x 2'); + } + /** * testProxy method * From 37303d9c37abe86fe2d8eeea75b3e9d2fcf5316d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 02:52:40 -0200 Subject: [PATCH 46/52] HttpResponse::getHeader() is more flexible to accept a custom header list. --- cake/libs/http_response.php | 16 ++++++++++------ cake/tests/cases/libs/http_response.test.php | 3 +++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cake/libs/http_response.php b/cake/libs/http_response.php index aea0133ae..77b7feb02 100644 --- a/cake/libs/http_response.php +++ b/cake/libs/http_response.php @@ -94,11 +94,14 @@ class HttpResponse implements ArrayAccess { * @param string $name Header name * @return mixed String if header exists or null */ - public function getHeader($name) { - if (isset($this->headers[$name])) { - return $this->headers[$name]; + public function getHeader($name, $headers = null) { + if (!is_array($headers)) { + $headers =& $this->headers; } - foreach ($this->headers as $key => $value) { + if (isset($headers[$name])) { + return $headers[$name]; + } + foreach ($headers as $key => $value) { if (strcasecmp($key, $name) == 0) { return $value; } @@ -278,12 +281,13 @@ class HttpResponse implements ArrayAccess { * @todo Make this 100% RFC 2965 confirm */ public function parseCookies($header) { - if (!isset($header['Set-Cookie'])) { + $cookieHeader = $this->getHeader('Set-Cookie', $header); + if (!$cookieHeader) { return false; } $cookies = array(); - foreach ((array)$header['Set-Cookie'] as $cookie) { + foreach ((array)$cookieHeader as $cookie) { if (strpos($cookie, '";"') !== false) { $cookie = str_replace('";"', "{__cookie_replace__}", $cookie); $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie)); diff --git a/cake/tests/cases/libs/http_response.test.php b/cake/tests/cases/libs/http_response.test.php index 54a92e1a4..4f54dc3f7 100644 --- a/cake/tests/cases/libs/http_response.test.php +++ b/cake/tests/cases/libs/http_response.test.php @@ -113,6 +113,9 @@ class HttpResponseTest extends CakeTestCase { $this->assertEqual($this->HttpResponse->getHeader('header'), 'value'); $this->assertEqual($this->HttpResponse->getHeader('Content-Type'), 'text/plain'); $this->assertIdentical($this->HttpResponse->getHeader(0), null); + + $this->assertEqual($this->HttpResponse->getHeader('foo', false), 'Bar'); + $this->assertEqual($this->HttpResponse->getHeader('foo', array('foo' => 'not from class')), 'not from class'); } /** From cf7aae791132a3ca418edad5894f52f14cb86826 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 11:05:37 -0200 Subject: [PATCH 47/52] Removed parseHeader from HttpSocket (it was not used). Moved the tests of parseHeader to HttpResponse. --- cake/libs/http_socket.php | 48 -------- cake/tests/cases/libs/http_response.test.php | 112 +++++++++++++++++++ cake/tests/cases/libs/http_socket.test.php | 106 ------------------ 3 files changed, 112 insertions(+), 154 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index a4fea03f3..cc28ae5fd 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -279,7 +279,6 @@ class HttpSocket extends CakeSocket { $cookies = null; if (is_array($this->request['header'])) { - $this->request['header'] = $this->_parseHeader($this->request['header']); if (!empty($this->request['cookies'])) { $cookies = $this->buildCookies($this->request['cookies']); } @@ -869,39 +868,6 @@ class HttpSocket extends CakeSocket { return $returnHeader; } -/** - * Parses an array based header. - * - * @param array $header Header as an indexed array (field => value) - * @return array Parsed header - */ - protected function _parseHeader($header) { - if (is_array($header)) { - return $header; - } elseif (!is_string($header)) { - return false; - } - - preg_match_all("/(.+):(.+)(?:(?_unescapeToken($field); - - if (!isset($header[$field])) { - $header[$field] = $value; - } else { - $header[$field] = array_merge((array)$header[$field], (array)$value); - } - } - return $header; - } - /** * Builds cookie headers for a request. * @@ -917,20 +883,6 @@ class HttpSocket extends CakeSocket { return $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic'); } -/** - * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs) - * - * @param string $token Token to unescape - * @param array $chars - * @return string Unescaped token - * @todo Test $chars parameter - */ - protected function _unescapeToken($token, $chars = null) { - $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/'; - $token = preg_replace($regex, '\\1', $token); - return $token; - } - /** * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs) * diff --git a/cake/tests/cases/libs/http_response.test.php b/cake/tests/cases/libs/http_response.test.php index 4f54dc3f7..63892535c 100644 --- a/cake/tests/cases/libs/http_response.test.php +++ b/cake/tests/cases/libs/http_response.test.php @@ -27,6 +27,16 @@ App::import('Core', 'HttpResponse'); */ class TestHttpResponse extends HttpResponse { +/** + * Convenience method for testing protected method + * + * @param array $header Header as an indexed array (field => value) + * @return array Parsed header + */ + public function parseHeader($header) { + return parent::_parseHeader($header); + } + /** * Convenience method for testing protected method * @@ -48,6 +58,26 @@ class TestHttpResponse extends HttpResponse { return parent::_decodeChunkedBody($body); } +/** + * Convenience method for testing protected method + * + * @param string $token Token to unescape + * @return string Unescaped token + */ + public function unescapeToken($token, $chars = null) { + return parent::_unescapeToken($token, $chars); + } + +/** + * Convenience method for testing protected method + * + * @param boolean $hex true to get them as HEX values, false otherwise + * @return array Escape chars + */ + public function tokenEscapeChars($hex = true, $chars = null) { + return parent::_tokenEscapeChars($hex, $chars); + } + } /** @@ -136,6 +166,65 @@ class HttpResponseTest extends CakeTestCase { $this->assertTrue($this->HttpResponse->isOk()); } +/** + * Test that HttpSocket::parseHeader can take apart a given (and valid) $header string and turn it into an array. + * + * @return void + */ + public function testParseHeader() { + $r = $this->HttpResponse->parseHeader(array('foo' => 'Bar', 'fOO-bAr' => 'quux')); + $this->assertEquals($r, array('foo' => 'Bar', 'fOO-bAr' => 'quux')); + + $r = $this->HttpResponse->parseHeader(true); + $this->assertEquals($r, false); + + $header = "Host: cakephp.org\t\r\n"; + $r = $this->HttpResponse->parseHeader($header); + $expected = array( + 'Host' => 'cakephp.org' + ); + $this->assertEquals($r, $expected); + + $header = "Date:Sat, 07 Apr 2007 10:10:25 GMT\r\nX-Powered-By: PHP/5.1.2\r\n"; + $r = $this->HttpResponse->parseHeader($header); + $expected = array( + 'Date' => 'Sat, 07 Apr 2007 10:10:25 GMT', + 'X-Powered-By' => 'PHP/5.1.2' + ); + $this->assertEquals($r, $expected); + + $header = "people: Jim,John\r\nfoo-LAND: Bar\r\ncAKe-PHP: rocks\r\n"; + $r = $this->HttpResponse->parseHeader($header); + $expected = array( + 'people' => 'Jim,John', + 'foo-LAND' => 'Bar', + 'cAKe-PHP' => 'rocks' + ); + $this->assertEquals($r, $expected); + + $header = "People: Jim,John,Tim\r\nPeople: Lisa,Tina,Chelsea\r\n"; + $r = $this->HttpResponse->parseHeader($header); + $expected = array( + 'People' => array('Jim,John,Tim', 'Lisa,Tina,Chelsea') + ); + $this->assertEquals($r, $expected); + + $header = "Multi-Line: I am a \r\nmulti line\t\r\nfield value.\r\nSingle-Line: I am not\r\n"; + $r = $this->HttpResponse->parseHeader($header); + $expected = array( + 'Multi-Line' => "I am a\r\nmulti line\r\nfield value.", + 'Single-Line' => 'I am not' + ); + $this->assertEquals($r, $expected); + + $header = "Esc\"@\"ped: value\r\n"; + $r = $this->HttpResponse->parseHeader($header); + $expected = array( + 'Esc@ped' => 'value' + ); + $this->assertEquals($r, $expected); + } + /** * testParseResponse method * @@ -339,6 +428,29 @@ class HttpResponseTest extends CakeTestCase { $this->assertEqual($cookies, $expected); } +/** + * Test that escaped token strings are properly unescaped by HttpSocket::unescapeToken + * + * @return void + */ + public function testUnescapeToken() { + $this->assertEquals($this->HttpResponse->unescapeToken('Foo'), 'Foo'); + + $escape = $this->HttpResponse->tokenEscapeChars(false); + foreach ($escape as $char) { + $token = 'My-special-"' . $char . '"-Token'; + $unescapedToken = $this->HttpResponse->unescapeToken($token); + $expectedToken = 'My-special-' . $char . '-Token'; + + $this->assertEquals($unescapedToken, $expectedToken, 'Test token unescaping for ASCII '.ord($char)); + } + + $token = 'Extreme-":"Token-" "-""""@"-test'; + $escapedToken = $this->HttpResponse->unescapeToken($token); + $expectedToken = 'Extreme-:Token- -"@-test'; + $this->assertEquals($expectedToken, $escapedToken); + } + /** * testArrayAccess * diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php index 45f0350c6..cefd649e2 100644 --- a/cake/tests/cases/libs/http_socket.test.php +++ b/cake/tests/cases/libs/http_socket.test.php @@ -122,16 +122,6 @@ class TestHttpSocket extends HttpSocket { return parent::_buildHeader($header, $mode); } -/** - * Convenience method for testing protected method - * - * @param array $header Header as an indexed array (field => value) - * @return array Parsed header - */ - public function parseHeader($header) { - return parent::_parseHeader($header); - } - /** * Convenience method for testing protected method * @@ -173,16 +163,6 @@ class TestHttpSocket extends HttpSocket { return parent::_escapeToken($token, $chars); } -/** - * Convenience method for testing protected method - * - * @param string $token Token to unescape - * @return string Unescaped token - */ - public function unescapeToken($token, $chars = null) { - return parent::_unescapeToken($token, $chars); - } - } /** @@ -1375,67 +1355,6 @@ class HttpSocketTest extends CakeTestCase { } -/** - * Test that HttpSocket::parseHeader can take apart a given (and valid) $header string and turn it into an array. - * - * @return void - */ - public function testParseHeader() { - $this->Socket->reset(); - - $r = $this->Socket->parseHeader(array('foo' => 'Bar', 'fOO-bAr' => 'quux')); - $this->assertEquals($r, array('foo' => 'Bar', 'fOO-bAr' => 'quux')); - - $r = $this->Socket->parseHeader(true); - $this->assertEquals($r, false); - - $header = "Host: cakephp.org\t\r\n"; - $r = $this->Socket->parseHeader($header); - $expected = array( - 'Host' => 'cakephp.org' - ); - $this->assertEquals($r, $expected); - - $header = "Date:Sat, 07 Apr 2007 10:10:25 GMT\r\nX-Powered-By: PHP/5.1.2\r\n"; - $r = $this->Socket->parseHeader($header); - $expected = array( - 'Date' => 'Sat, 07 Apr 2007 10:10:25 GMT', - 'X-Powered-By' => 'PHP/5.1.2' - ); - $this->assertEquals($r, $expected); - - $header = "people: Jim,John\r\nfoo-LAND: Bar\r\ncAKe-PHP: rocks\r\n"; - $r = $this->Socket->parseHeader($header); - $expected = array( - 'people' => 'Jim,John', - 'foo-LAND' => 'Bar', - 'cAKe-PHP' => 'rocks' - ); - $this->assertEquals($r, $expected); - - $header = "People: Jim,John,Tim\r\nPeople: Lisa,Tina,Chelsea\r\n"; - $r = $this->Socket->parseHeader($header); - $expected = array( - 'People' => array('Jim,John,Tim', 'Lisa,Tina,Chelsea') - ); - $this->assertEquals($r, $expected); - - $header = "Multi-Line: I am a \r\nmulti line\t\r\nfield value.\r\nSingle-Line: I am not\r\n"; - $r = $this->Socket->parseHeader($header); - $expected = array( - 'Multi-Line' => "I am a\r\nmulti line\r\nfield value.", - 'Single-Line' => 'I am not' - ); - $this->assertEquals($r, $expected); - - $header = "Esc\"@\"ped: value\r\n"; - $r = $this->Socket->parseHeader($header); - $expected = array( - 'Esc@ped' => 'value' - ); - $this->assertEquals($r, $expected); - } - /** * testBuildCookies method * @@ -1507,31 +1426,6 @@ class HttpSocketTest extends CakeTestCase { $this->assertEquals($expectedToken, $escapedToken); } -/** - * Test that escaped token strings are properly unescaped by HttpSocket::unescapeToken - * - * @return void - */ - public function testUnescapeToken() { - $this->Socket->reset(); - - $this->assertEquals($this->Socket->unescapeToken('Foo'), 'Foo'); - - $escape = $this->Socket->tokenEscapeChars(false); - foreach ($escape as $char) { - $token = 'My-special-"' . $char . '"-Token'; - $unescapedToken = $this->Socket->unescapeToken($token); - $expectedToken = 'My-special-' . $char . '-Token'; - - $this->assertEquals($unescapedToken, $expectedToken, 'Test token unescaping for ASCII '.ord($char)); - } - - $token = 'Extreme-":"Token-" "-""""@"-test'; - $escapedToken = $this->Socket->unescapeToken($token); - $expectedToken = 'Extreme-:Token- -"@-test'; - $this->assertEquals($expectedToken, $escapedToken); - } - /** * This tests asserts HttpSocket::reset() resets a HttpSocket instance to it's initial state (before Object::__construct * got executed) From 2cbece678495273aa5504b1362b5f252309dc7e9 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 11:11:36 -0200 Subject: [PATCH 48/52] Removed the lineBreak attribute, this is fixed in RFC. --- cake/libs/http_socket.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index cc28ae5fd..a521ea9c5 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -104,13 +104,6 @@ class HttpSocket extends CakeSocket { ) ); -/** - * String that represents a line break. - * - * @var string - */ - public $lineBreak = "\r\n"; - /** * Authentication settings * @@ -809,7 +802,7 @@ class HttpSocket extends CakeSocket { if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { throw new Exception(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.'), join(',', $asteriskMethods))); } - return $request['method'] . ' ' . $request['uri'] . ' ' . $versionToken . $this->lineBreak; + return $request['method'] . ' ' . $request['uri'] . ' ' . $versionToken . "\r\n"; } /** @@ -862,7 +855,7 @@ class HttpSocket extends CakeSocket { $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content); $field = $this->_escapeToken($field); - $returnHeader .= $field . ': ' . $contents . $this->lineBreak; + $returnHeader .= $field . ': ' . $contents . "\r\n"; } } return $returnHeader; From 296aef2c112ff8c6d6783d282e22895ef86e125d Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Tue, 14 Dec 2010 11:13:44 -0200 Subject: [PATCH 49/52] Removed the import. It is called in request. --- cake/libs/http_socket.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php index a521ea9c5..a5e66964a 100644 --- a/cake/libs/http_socket.php +++ b/cake/libs/http_socket.php @@ -19,7 +19,6 @@ */ App::import('Core', 'CakeSocket'); App::import('Core', 'Router'); -App::import('Lib', 'HttpResponse'); /** * Cake network socket connection class. From 99407282e5c6bfda96b7ff311138b6007acc7852 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 15 Dec 2010 01:51:00 -0200 Subject: [PATCH 50/52] Updated the config of auth in digest method. --- cake/libs/http/digest_authentication.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cake/libs/http/digest_authentication.php b/cake/libs/http/digest_authentication.php index ba146e2fc..adcc9dd9d 100644 --- a/cake/libs/http/digest_authentication.php +++ b/cake/libs/http/digest_authentication.php @@ -53,10 +53,10 @@ class DigestAuthentication { */ protected static function _getServerInformation(HttpSocket $http, &$authInfo) { $originalRequest = $http->request; - $http->setAuthConfig(false); + $http->configAuth(false); $http->request($http->request); $http->request = $originalRequest; - $http->setAuthConfig('Digest', $authInfo); + $http->configAuth('Digest', $authInfo); if (empty($http->response['header']['WWW-Authenticate'])) { return false; From 575da672b0c81305eb22fe2ba46bd0d1a164a760 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 15 Dec 2010 01:54:37 -0200 Subject: [PATCH 51/52] Fixed basic proxy test. --- cake/tests/cases/libs/http/basic_authentication.test.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cake/tests/cases/libs/http/basic_authentication.test.php b/cake/tests/cases/libs/http/basic_authentication.test.php index 7ba642b64..f886a6686 100644 --- a/cake/tests/cases/libs/http/basic_authentication.test.php +++ b/cake/tests/cases/libs/http/basic_authentication.test.php @@ -53,13 +53,13 @@ class BasicMethodTest extends CakeTestCase { */ public function testProxyAuthentication() { $http = new HttpSocket(); - $http->request['proxy'] = array( + $proxy = array( 'method' => 'Basic', 'user' => 'mark', 'pass' => 'secret' ); - BasicAuthentication::proxyAuthentication($http); + BasicAuthentication::proxyAuthentication($http, $proxy); $this->assertEqual($http->request['header']['Proxy-Authorization'], 'Basic bWFyazpzZWNyZXQ='); } From ad5e7248c56b4e40f53ebe7fb2aa217e52b60d10 Mon Sep 17 00:00:00 2001 From: Juan Basso Date: Wed, 15 Dec 2010 01:54:48 -0200 Subject: [PATCH 52/52] Fixed documentation. --- cake/libs/http/digest_authentication.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cake/libs/http/digest_authentication.php b/cake/libs/http/digest_authentication.php index adcc9dd9d..21f7ea8e6 100644 --- a/cake/libs/http/digest_authentication.php +++ b/cake/libs/http/digest_authentication.php @@ -32,7 +32,6 @@ class DigestAuthentication { * @param HttpSocket $http * @param array $authInfo * @return void - * @throws Exception * @link http://www.ietf.org/rfc/rfc2617.txt */ public static function authentication(HttpSocket $http, &$authInfo) {