From 642ca3862ba1a3b6cbd26db21f30caa2249bb8e4 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Tue, 27 Oct 2015 12:21:30 -0700 Subject: [PATCH] Normalize header case for FileBackend operations Normalize all headers to lower case at the start of the FileBackend operation methods. This makes it easy for subclasses to check for certain headers, e.g. content-type. Change-Id: Ia69976326d17a51bcaa61f2781aa669ae7bd9c28 --- includes/filebackend/FileBackendStore.php | 21 ++++++++----- .../includes/filebackend/FileBackendTest.php | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/includes/filebackend/FileBackendStore.php b/includes/filebackend/FileBackendStore.php index e5ce968a97..4ec81ecde1 100644 --- a/includes/filebackend/FileBackendStore.php +++ b/includes/filebackend/FileBackendStore.php @@ -1066,7 +1066,7 @@ abstract class FileBackendStore extends FileBackend { $status = Status::newGood(); // Fix up custom header name/value pairs... - $ops = array_map( array( $this, 'stripInvalidHeadersFromOp' ), $ops ); + $ops = array_map( array( $this, 'sanitizeOpHeaders' ), $ops ); // Build up a list of FileOps... $performOps = $this->getOperationsInternal( $ops ); @@ -1133,7 +1133,7 @@ abstract class FileBackendStore extends FileBackend { $status = Status::newGood(); // Fix up custom header name/value pairs... - $ops = array_map( array( $this, 'stripInvalidHeadersFromOp' ), $ops ); + $ops = array_map( array( $this, 'sanitizeOpHeaders' ), $ops ); // Clear any file cache entries $this->clearCache(); @@ -1230,7 +1230,9 @@ abstract class FileBackendStore extends FileBackend { } /** - * Strip long HTTP headers from a file operation. + * Normalize and filter HTTP headers from a file operation + * + * This normalizes and strips long HTTP headers from a file operation. * Most headers are just numbers, but some are allowed to be long. * This function is useful for cleaning up headers and avoiding backend * specific errors, especially in the middle of batch file operations. @@ -1238,18 +1240,21 @@ abstract class FileBackendStore extends FileBackend { * @param array $op Same format as doOperation() * @return array */ - protected function stripInvalidHeadersFromOp( array $op ) { - static $longs = array( 'Content-Disposition' ); + protected function sanitizeOpHeaders( array $op ) { + static $longs = array( 'content-disposition' ); + if ( isset( $op['headers'] ) ) { // op sets HTTP headers + $newHeaders = array(); foreach ( $op['headers'] as $name => $value ) { + $name = strtolower( $name ); $maxHVLen = in_array( $name, $longs ) ? INF : 255; if ( strlen( $name ) > 255 || strlen( $value ) > $maxHVLen ) { trigger_error( "Header '$name: $value' is too long." ); - unset( $op['headers'][$name] ); - } elseif ( !strlen( $value ) ) { - $op['headers'][$name] = ''; // null/false => "" + } else { + $newHeaders[$name] = strlen( $value ) ? $value : ''; // null/false => "" } } + $op['headers'] = $newHeaders; } return $op; diff --git a/tests/phpunit/includes/filebackend/FileBackendTest.php b/tests/phpunit/includes/filebackend/FileBackendTest.php index 0d15b75bfb..e7d092fad0 100644 --- a/tests/phpunit/includes/filebackend/FileBackendTest.php +++ b/tests/phpunit/includes/filebackend/FileBackendTest.php @@ -2495,6 +2495,37 @@ class FileBackendTest extends MediaWikiTestCase { ); } + public function testSanitizeOpHeaders() { + $be = TestingAccessWrapper::newFromObject( new MemoryFileBackend( array( + 'name' => 'localtesting', + 'wikiId' => wfWikiID() + ) ) ); + + $name = wfRandomString( 300 ); + + $input = array( + 'headers' => array( + 'content-Disposition' => FileBackend::makeContentDisposition( 'inline', $name ), + 'Content-dUration' => 25.6, + 'X-LONG-VALUE' => str_pad( '0', 300 ), + 'CONTENT-LENGTH' => 855055, + ) + ); + $expected = array( + 'headers' => array( + 'content-disposition' => FileBackend::makeContentDisposition( 'inline', $name ), + 'content-duration' => 25.6, + 'content-length' => 855055 + ) + ); + + MediaWiki\suppressWarnings(); + $actual = $be->sanitizeOpHeaders( $input ); + MediaWiki\restoreWarnings(); + + $this->assertEquals( $expected, $actual, "Header sanitized properly" ); + } + // helper function private function listToArray( $iter ) { return is_array( $iter ) ? $iter : iterator_to_array( $iter ); -- 2.20.1