From 8cbf9759435a0715e0695cc0f7f0a08fd7955997 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 16 Nov 2014 23:34:34 -0500 Subject: [PATCH 01/10] Gracefully handle invalid chunks in HttpSocket When invalid chunks are detected we should assume the server is incorrect and handle the remaining content as a single large chunk. Refs #5140 --- lib/Cake/Network/Http/HttpSocketResponse.php | 23 ++++++++++--------- .../Case/Network/Http/HttpResponseTest.php | 6 ++--- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/Cake/Network/Http/HttpSocketResponse.php b/lib/Cake/Network/Http/HttpSocketResponse.php index 03474f385..99d875c31 100644 --- a/lib/Cake/Network/Http/HttpSocketResponse.php +++ b/lib/Cake/Network/Http/HttpSocketResponse.php @@ -221,28 +221,29 @@ class HttpSocketResponse implements ArrayAccess { $chunkLength = null; while ($chunkLength !== 0) { - if (!preg_match('/^([0-9a-f]+) *(?:;(.+)=(.+))?(?:\r\n|\n)/iU', $body, $match)) { - throw new SocketException(__d('cake_dev', 'HttpSocket::_decodeChunkedBody - Could not parse malformed chunk.')); + if (!preg_match('/^([0-9a-f]+)[ ]*(?:;(.+)=(.+))?(?:\r\n|\n)/iU', $body, $match)) { + // Handle remaining invalid data as one big chunk. + preg_match('/^(.*?)\r\n/', $body, $invalidMatch); + $length = isset($invalidMatch[1]) ? strlen($invalidMatch[1]) : 0; + $match = array( + 0 => '', + 1 => dechex($length) + ); } - $chunkSize = 0; $hexLength = 0; - $chunkExtensionValue = ''; if (isset($match[0])) { $chunkSize = $match[0]; } if (isset($match[1])) { $hexLength = $match[1]; } - if (isset($match[3])) { - $chunkExtensionValue = $match[3]; - } - $body = substr($body, strlen($chunkSize)); $chunkLength = hexdec($hexLength); - $chunk = substr($body, 0, $chunkLength); - $decodedBody .= $chunk; - if ($chunkLength !== 0) { + $body = substr($body, strlen($chunkSize)); + + $decodedBody .= substr($body, 0, $chunkLength); + if ($chunkLength) { $body = substr($body, $chunkLength + strlen("\r\n")); } } diff --git a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php index 54493a6b8..502340094 100644 --- a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php @@ -15,7 +15,6 @@ * @since CakePHP(tm) v 1.2.0.4206 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - App::uses('HttpResponse', 'Network/Http'); /** @@ -453,12 +452,13 @@ class HttpResponseTest extends CakeTestCase { /** * testDecodeChunkedBodyError method * - * @expectedException SocketException * @return void */ public function testDecodeChunkedBodyError() { $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n"; - $this->HttpResponse->decodeChunkedBody($encoded); + $result = $this->HttpResponse->decodeChunkedBody($encoded); + $expected = "This is a chunked message\nThat is cool\n"; + $this->assertEquals($expected, $result['body']); } /** From ddc3eee84cc85eeabf51681ee501cbbd8818590a Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 17 Nov 2014 22:47:07 -0500 Subject: [PATCH 02/10] Use name() instead of hand quoting sequence names. By using name() we can easily accomodate postgres sequences that exist out side of sequences. Fixes #4168 --- lib/Cake/Model/Datasource/Database/Postgres.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Postgres.php b/lib/Cake/Model/Datasource/Database/Postgres.php index bc2e3e075..74f9a9798 100644 --- a/lib/Cake/Model/Datasource/Database/Postgres.php +++ b/lib/Cake/Model/Datasource/Database/Postgres.php @@ -353,8 +353,8 @@ class Postgres extends DboSource { if ($this->execute('DELETE FROM ' . $this->fullTableName($table))) { if (isset($this->_sequenceMap[$table]) && $reset != true) { foreach ($this->_sequenceMap[$table] as $sequence) { - list($schema, $sequence) = explode('.', $sequence); - $this->_execute("ALTER SEQUENCE \"{$schema}\".\"{$sequence}\" RESTART WITH 1"); + $quoted = $this->name($sequence); + $this->_execute("ALTER SEQUENCE {$sequence} RESTART WITH 1"); } } return true; From bae556e73fc18fbf081eba7862fb3767be1a5e2a Mon Sep 17 00:00:00 2001 From: ndm2 Date: Tue, 18 Nov 2014 17:26:31 +0100 Subject: [PATCH 03/10] Prevent zero only lines from being emptied --- lib/Cake/Network/Email/CakeEmail.php | 2 +- .../Test/Case/Network/Email/CakeEmailTest.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index eb5b3c9b7..61dc597b3 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -1359,7 +1359,7 @@ class CakeEmail { $cut = ($wrapLength == CakeEmail::LINE_LENGTH_MUST); foreach ($lines as $line) { - if (empty($line)) { + if (empty($line) && $line !== '0') { $formatted[] = ''; continue; } diff --git a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php index 23dd78bd6..1dc125fea 100644 --- a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php +++ b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php @@ -2416,6 +2416,25 @@ HTML; $this->assertEquals($expected, $result['message']); } +/** + * testZeroOnlyLinesNotBeingEmptied() + * + * @return void + */ + public function testZeroOnlyLinesNotBeingEmptied() { + $message = "Lorem\r\n0\r\n0\r\nipsum"; + + $this->CakeEmail->reset(); + $this->CakeEmail->transport('Debug'); + $this->CakeEmail->from('cake@cakephp.org'); + $this->CakeEmail->to('cake@cakephp.org'); + $this->CakeEmail->subject('Wordwrap Test'); + $this->CakeEmail->config(array('empty')); + $result = $this->CakeEmail->send($message); + $expected = "{$message}\r\n\r\n"; + $this->assertEquals($expected, $result['message']); + } + /** * CakeEmailTest::assertLineLengths() * From f2c84602b914f4863afee9b8a208bcb34dd13004 Mon Sep 17 00:00:00 2001 From: Mischa ter Smitten Date: Tue, 18 Nov 2014 21:26:57 +0100 Subject: [PATCH 04/10] Bugfix for: Memcached Engine segfaults with memcached client version < 2.0.0 Fixes #5028 --- lib/Cake/Cache/Engine/MemcachedEngine.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Cache/Engine/MemcachedEngine.php b/lib/Cake/Cache/Engine/MemcachedEngine.php index 319cd3ea0..f910ffbf4 100644 --- a/lib/Cake/Cache/Engine/MemcachedEngine.php +++ b/lib/Cake/Cache/Engine/MemcachedEngine.php @@ -104,7 +104,11 @@ class MemcachedEngine extends CacheEngine { return true; } - $this->_Memcached = new Memcached($this->settings['persistent'] ? (string)$this->settings['persistent'] : null); + if (!$this->settings['persistent']) { + $this->_Memcached = new Memcached(); + } else { + $this->_Memcached = new Memcached((string)$this->settings['persistent']); + } $this->_setOptions(); if (count($this->_Memcached->getServerList())) { From 23c5805763e3603966f6876a77f1418cc57b16de Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 18 Nov 2014 22:03:21 -0500 Subject: [PATCH 05/10] Fix mistake with previous commit. I forgot to put the correct variable into the query like a doofus. Refs #4168 --- lib/Cake/Model/Datasource/Database/Postgres.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Model/Datasource/Database/Postgres.php b/lib/Cake/Model/Datasource/Database/Postgres.php index 74f9a9798..475ab784c 100644 --- a/lib/Cake/Model/Datasource/Database/Postgres.php +++ b/lib/Cake/Model/Datasource/Database/Postgres.php @@ -354,7 +354,7 @@ class Postgres extends DboSource { if (isset($this->_sequenceMap[$table]) && $reset != true) { foreach ($this->_sequenceMap[$table] as $sequence) { $quoted = $this->name($sequence); - $this->_execute("ALTER SEQUENCE {$sequence} RESTART WITH 1"); + $this->_execute("ALTER SEQUENCE {$quoted} RESTART WITH 1"); } } return true; From b6caddcf49967a3ce289178d156645a691b1dfff Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Nov 2014 12:21:28 -0500 Subject: [PATCH 06/10] Fix file_map being regenerated on every request. A mistake a few months ago caused the file_map to always be regenerated as Configure::write() would cause both Configure and Hash to be loaded, invalidating the file map. By entering bootstrap mode earlier we can avoid that. Refs #5229 --- lib/Cake/bootstrap.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Cake/bootstrap.php b/lib/Cake/bootstrap.php index 303d36083..4ad77a21b 100644 --- a/lib/Cake/bootstrap.php +++ b/lib/Cake/bootstrap.php @@ -148,6 +148,8 @@ App::uses('Cache', 'Cache'); App::uses('Object', 'Core'); App::uses('Multibyte', 'I18n'); +App::$bootstrapping = true; + /** * Full URL prefix */ @@ -170,8 +172,6 @@ Configure::write('App.imageBaseUrl', IMAGES_URL); Configure::write('App.cssBaseUrl', CSS_URL); Configure::write('App.jsBaseUrl', JS_URL); -App::$bootstrapping = true; - Configure::bootstrap(isset($boot) ? $boot : true); if (function_exists('mb_internal_encoding')) { From bb5088cda3edea9f2361599f65a6ba02ba5523cd Mon Sep 17 00:00:00 2001 From: Lawrence Barsanti Date: Mon, 24 Nov 2014 14:51:36 -0500 Subject: [PATCH 07/10] PHPUnit can be included as a phar file. No need to install phpunit in order to run unit tests. Simply place 'phpunit.phar' in the vendors folder. --- lib/Cake/TestSuite/CakeTestRunner.php | 4 +++- lib/Cake/TestSuite/CakeTestSuiteCommand.php | 4 +++- lib/Cake/TestSuite/CakeTestSuiteDispatcher.php | 6 ++++++ lib/Cake/TestSuite/Reporter/CakeBaseReporter.php | 4 +++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/Cake/TestSuite/CakeTestRunner.php b/lib/Cake/TestSuite/CakeTestRunner.php index 8e2fc4730..3aafdc198 100644 --- a/lib/Cake/TestSuite/CakeTestRunner.php +++ b/lib/Cake/TestSuite/CakeTestRunner.php @@ -15,7 +15,9 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -require_once 'PHPUnit/TextUI/TestRunner.php'; +if (!defined('__PHPUNIT_PHAR__')) { + require_once 'PHPUnit/TextUI/TestRunner.php'; +} App::uses('CakeFixtureManager', 'TestSuite/Fixture'); diff --git a/lib/Cake/TestSuite/CakeTestSuiteCommand.php b/lib/Cake/TestSuite/CakeTestSuiteCommand.php index 363a0057a..c4134c535 100644 --- a/lib/Cake/TestSuite/CakeTestSuiteCommand.php +++ b/lib/Cake/TestSuite/CakeTestSuiteCommand.php @@ -16,7 +16,9 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -require_once 'PHPUnit/TextUI/Command.php'; +if (!defined('__PHPUNIT_PHAR__')) { + require_once 'PHPUnit/TextUI/Command.php'; +} App::uses('CakeTestRunner', 'TestSuite'); App::uses('CakeTestLoader', 'TestSuite'); diff --git a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php index 725df5eb1..8b8b1e3ca 100644 --- a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php +++ b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php @@ -151,6 +151,12 @@ class CakeTestSuiteDispatcher { } elseif (is_dir($vendor . DS . 'PHPUnit')) { ini_set('include_path', $vendor . PATH_SEPARATOR . ini_get('include_path')); break; + } elseif (is_file($vendor . DS . 'phpunit.phar')) { + $backup = $GLOBALS['_SERVER']['SCRIPT_NAME']; + $GLOBALS['_SERVER']['SCRIPT_NAME'] = '-'; + $included = include_once $vendor . DS . 'phpunit.phar'; + $GLOBALS['_SERVER']['SCRIPT_NAME'] = $backup; + return $included; } } include 'PHPUnit' . DS . 'Autoload.php'; diff --git a/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php b/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php index 3974bee2b..8cd1ca7a4 100644 --- a/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php +++ b/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php @@ -15,7 +15,9 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -require_once 'PHPUnit/TextUI/ResultPrinter.php'; + if (!defined('__PHPUNIT_PHAR__')) { + require_once 'PHPUnit/TextUI/ResultPrinter.php'; +} /** * CakeBaseReporter contains common reporting features used in the CakePHP Test suite From 9631b54244c16c9d183cc02416159d9dc935ad71 Mon Sep 17 00:00:00 2001 From: Lawrence Barsanti Date: Mon, 24 Nov 2014 16:36:43 -0500 Subject: [PATCH 08/10] remove space --- lib/Cake/TestSuite/Reporter/CakeBaseReporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php b/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php index 8cd1ca7a4..c89ead4df 100644 --- a/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php +++ b/lib/Cake/TestSuite/Reporter/CakeBaseReporter.php @@ -15,7 +15,7 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - if (!defined('__PHPUNIT_PHAR__')) { +if (!defined('__PHPUNIT_PHAR__')) { require_once 'PHPUnit/TextUI/ResultPrinter.php'; } From 1e6d22b8cbdde19d70b8162390d2a3644fd74bb5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 24 Nov 2014 22:23:12 -0500 Subject: [PATCH 09/10] Make the version option function as intended. The version option is documented but does not work. While this 'breaks' behavior, it also fixes what I think is a more important issue. Refs #5234 --- lib/Cake/Network/Http/HttpSocket.php | 6 +++--- .../Test/Case/Network/Http/HttpSocketTest.php | 21 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/Cake/Network/Http/HttpSocket.php b/lib/Cake/Network/Http/HttpSocket.php index bf4e6959e..09e8b0c30 100644 --- a/lib/Cake/Network/Http/HttpSocket.php +++ b/lib/Cake/Network/Http/HttpSocket.php @@ -877,11 +877,10 @@ class HttpSocket extends CakeSocket { * Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs. * * @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 SocketException */ - protected function _buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { + protected function _buildRequestLine($request = array()) { $asteriskMethods = array('OPTIONS'); if (is_string($request)) { @@ -907,7 +906,8 @@ class HttpSocket extends CakeSocket { if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { throw new SocketException(__d('cake_dev', 'HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', implode(',', $asteriskMethods))); } - return $request['method'] . ' ' . $request['uri'] . ' ' . $versionToken . "\r\n"; + $version = isset($request['version']) ? $request['version'] : '1.1'; + return $request['method'] . ' ' . $request['uri'] . ' HTTP/' . $version . "\r\n"; } /** diff --git a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php index 795ef08e8..1ca68b4e0 100644 --- a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php @@ -138,8 +138,8 @@ class TestHttpSocket extends HttpSocket { * @param string $versionToken The version token to use, defaults to HTTP/1.1 * @return string Request line */ - public function buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { - return parent::_buildRequestLine($request, $versionToken); + public function buildRequestLine($request = array()) { + return parent::_buildRequestLine($request); } /** @@ -525,6 +525,7 @@ class HttpSocketTest extends CakeTestCase { ), array( 'request' => array( + 'version' => '1.0', 'method' => 'POST', 'uri' => 'https://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'), @@ -532,6 +533,8 @@ class HttpSocketTest extends CakeTestCase { ), 'expectation' => array( 'request' => array( + 'version' => '1.0', + 'line' => "POST /posts/add HTTP/1.0\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\nCookie: foo=bar\r\n", 'cookies' => array( 'foo' => array('value' => 'bar'), @@ -1245,9 +1248,6 @@ class HttpSocketTest extends CakeTestCase { $r = $this->Socket->buildRequestLine($request); $this->assertEquals("GET /search?q=socket HTTP/1.1\r\n", $r); - $r = $this->Socket->buildRequestLine($request, 'CAKE-HTTP/0.1'); - $this->assertEquals("GET /search?q=socket CAKE-HTTP/0.1\r\n", $r); - $request = array('method' => 'OPTIONS', 'uri' => '*'); $r = $this->Socket->buildRequestLine($request); $this->assertEquals("OPTIONS * HTTP/1.1\r\n", $r); @@ -1259,6 +1259,17 @@ class HttpSocketTest extends CakeTestCase { $r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n"); $this->assertEquals("GET * HTTP/1.1\r\n", $r); + + $request = array( + 'version' => '1.0', + 'method' => 'GET', + 'uri' => array( + 'path' => '/search', + 'query' => array('q' => 'socket') + ) + ); + $r = $this->Socket->buildRequestLine($request); + $this->assertEquals("GET /search?q=socket HTTP/1.0\r\n", $r); } /** From 1f4402cd4c448571af68b9b694729f29f9ba45f0 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Sadakata Date: Wed, 26 Nov 2014 17:25:10 +0900 Subject: [PATCH 10/10] check ob_get_level() before ob_flush() --- lib/Cake/Network/CakeResponse.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index a1796ee80..bd5890e61 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -1507,7 +1507,9 @@ class CakeResponse { protected function _flushBuffer() { //@codingStandardsIgnoreStart @flush(); - @ob_flush(); + if (ob_get_level()) { + @ob_flush(); + } //@codingStandardsIgnoreEnd }