Do not strip Content-Type header for POST requests to swift
[lhc/web/wiklou.git] / includes / libs / filebackend / SwiftFileBackend.php
index 4212ff5..a3f121e 100644 (file)
@@ -158,7 +158,7 @@ class SwiftFileBackend extends FileBackendStore {
        protected function resolveContainerPath( $container, $relStoragePath ) {
                if ( !mb_check_encoding( $relStoragePath, 'UTF-8' ) ) {
                        return null; // not UTF-8, makes it hard to use CF and the swift HTTP API
-               } elseif ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
+               } elseif ( strlen( rawurlencode( $relStoragePath ) ) > 1024 ) {
                        return null; // too long for Swift
                }
 
@@ -181,6 +181,29 @@ class SwiftFileBackend extends FileBackendStore {
         * @param array $params
         * @return array Sanitized value of 'headers' field in $params
         */
+       protected function sanitizeHdrsStrict( array $params ) {
+               if ( !isset( $params['headers'] ) ) {
+                       return [];
+               }
+               $headers = $this->getCustomHeaders( $params ['headers'] );
+               if ( isset( $headers[ 'content-type' ] ) ) {
+                       unset( $headers[ 'content-type' ] );
+               }
+               return $headers;
+       }
+
+       /**
+        * Sanitize and filter the custom headers from a $params array.
+        * Only allows certain "standard" Content- and X-Content- headers.
+        *
+        * When POSTing data, libcurl adds Content-Type: application/x-www-form-urlencoded
+        * if Content-Type is not set, which overwrites the stored Content-Type header
+        * in Swift - therefore for POSTing data do not strip the Content-Type header (the
+        * previously-stored header that has been already read back from swift is sent)
+        *
+        * @param array $params
+        * @return array Sanitized value of 'headers' field in $params
+        */
        protected function sanitizeHdrs( array $params ) {
                return isset( $params['headers'] )
                        ? $this->getCustomHeaders( $params['headers'] )
@@ -197,7 +220,7 @@ class SwiftFileBackend extends FileBackendStore {
                // Normalize casing, and strip out illegal headers
                foreach ( $rawHeaders as $name => $value ) {
                        $name = strtolower( $name );
-                       if ( preg_match( '/^content-(type|length)$/', $name ) ) {
+                       if ( preg_match( '/^content-length$/', $name ) ) {
                                continue; // blacklisted
                        } elseif ( preg_match( '/^(x-)?content-/', $name ) ) {
                                $headers[$name] = $value; // allowed
@@ -276,7 +299,7 @@ class SwiftFileBackend extends FileBackendStore {
                                'etag' => md5( $params['content'] ),
                                'content-type' => $contentType,
                                'x-object-meta-sha1base36' => $sha1Hash
-                       ] + $this->sanitizeHdrs( $params ),
+                       ] + $this->sanitizeHdrsStrict( $params ),
                        'body' => $params['content']
                ] ];
 
@@ -340,7 +363,7 @@ class SwiftFileBackend extends FileBackendStore {
                                'etag' => md5_file( $params['src'] ),
                                'content-type' => $contentType,
                                'x-object-meta-sha1base36' => $sha1Hash
-                       ] + $this->sanitizeHdrs( $params ),
+                       ] + $this->sanitizeHdrsStrict( $params ),
                        'body' => $handle // resource
                ] ];
 
@@ -391,7 +414,7 @@ class SwiftFileBackend extends FileBackendStore {
                        'headers' => [
                                'x-copy-from' => '/' . rawurlencode( $srcCont ) .
                                        '/' . str_replace( "%2F", "/", rawurlencode( $srcRel ) )
-                       ] + $this->sanitizeHdrs( $params ), // extra headers merged into object
+                       ] + $this->sanitizeHdrsStrict( $params ), // extra headers merged into object
                ] ];
 
                $method = __METHOD__;
@@ -440,7 +463,7 @@ class SwiftFileBackend extends FileBackendStore {
                                'headers' => [
                                        'x-copy-from' => '/' . rawurlencode( $srcCont ) .
                                                '/' . str_replace( "%2F", "/", rawurlencode( $srcRel ) )
-                               ] + $this->sanitizeHdrs( $params ) // extra headers merged into object
+                               ] + $this->sanitizeHdrsStrict( $params ) // extra headers merged into object
                        ]
                ];
                if ( "{$srcCont}/{$srcRel}" !== "{$dstCont}/{$dstRel}" ) {
@@ -714,7 +737,8 @@ class SwiftFileBackend extends FileBackendStore {
 
                /** @noinspection PhpUnusedLocalVariableInspection */
                $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
-               $this->logger->error( __METHOD__ . ": $path was not stored with SHA-1 metadata." );
+               $this->logger->error( __METHOD__ . ": {path} was not stored with SHA-1 metadata.",
+                       [ 'path' => $path ] );
 
                $objHdrs['x-object-meta-sha1base36'] = false;
 
@@ -754,7 +778,8 @@ class SwiftFileBackend extends FileBackendStore {
                        }
                }
 
-               $this->logger->error( __METHOD__ . ": unable to set SHA-1 metadata for $path" );
+               $this->logger->error( __METHOD__ . ': unable to set SHA-1 metadata for {path}',
+                       [ 'path' => $path ] );
 
                return $objHdrs; // failed
        }
@@ -1360,7 +1385,8 @@ class SwiftFileBackend extends FileBackendStore {
 
                if ( $rcode != 204 && $rcode !== 202 ) {
                        $status->fatal( 'backend-fail-internal', $this->name );
-                       $this->logger->error( __METHOD__ . ': unexpected rcode value (' . $rcode . ')' );
+                       $this->logger->error( __METHOD__ . ': unexpected rcode value ({rcode})',
+                               [ 'rcode' => $rcode ] );
                }
 
                return $status;
@@ -1771,10 +1797,18 @@ class SwiftFileBackend extends FileBackendStore {
                if ( $code == 401 ) { // possibly a stale token
                        $this->srvCache->delete( $this->getCredsCacheKey( $this->swiftUser ) );
                }
-               $this->logger->error(
-                       "HTTP $code ($desc) in '{$func}' (given '" . FormatJson::encode( $params ) . "')" .
-                       ( $err ? ": $err" : "" )
-               );
+               $msg = "HTTP {code} ({desc}) in '{func}' (given '{params}')";
+               $msgParams = [
+                       'code'   => $code,
+                       'desc'   => $desc,
+                       'func'   => $func,
+                       'req_params' => FormatJson::encode( $params ),
+               ];
+               if ( $err ) {
+                       $msg .= ': {err}';
+                       $msgParams['err'] = $err;
+               }
+               $this->logger->error( $msg, $msgParams );
        }
 }