diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 02ff62a7b..05da83681 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -82,6 +82,30 @@ final class StreamContextFactory $options = array_replace_recursive($options, $defaultOptions); + if (isset($options['http']['header'])) { + $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']); + } + return stream_context_create($options, $defaultParams); } + + /** + * A bug in PHP prevents the headers from correctly beeing sent when a content-type header is present and + * NOT at the end of the array + * + * This method fixes the array by moving the content-type header to the end + * + * @link https://bugs.php.net/bug.php?id=61548 + * @param $header + * @return array + * @author Markus Tacker + */ + public static function fixHttpHeaderField($header) + { + if (!is_array($header)) $header = explode("\r\n", $header); + uasort($header, function ($el) { + return preg_match('/^content-type/i', $el) ? 1 : -1; + }); + return $header; + } } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 3d728ffeb..f185176b3 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -133,4 +133,28 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase array('ssl://proxyserver:8443', 'https://proxyserver:8443'), ); } + + /** + * @author Markus Tacker + */ + public function testEnsureThatfixHttpHeaderFieldMovesContentTypeToEndOfOptions() + { + $options = array( + 'http' => array( + 'header' => "X-Foo: bar\r\nContent-Type: application/json\r\nAuthorization: Basic aW52YWxpZA==" + ) + ); + $expectedOptions = array( + 'http' => array( + 'header' => array( + "X-Foo: bar", + "Authorization: Basic aW52YWxpZA==", + "Content-Type: application/json" + ) + ) + ); + $context = StreamContextFactory::getContext($options); + $ctxoptions = stream_context_get_options($context); + $this->assertEquals(join("\n", $ctxoptions['http']['header']), join("\n", $expectedOptions['http']['header'])); + } }