diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 87d722b91e4d..854746e39262 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -573,6 +573,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode) ->end() ->prototype('scalar')->end() ->end() + ->scalarNode('is_strict_protocol')->defaultValue(false)->end() ->end() ->validate() ->ifTrue(function ($v) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c65c1f8e740b..1d39b9c4b2ab 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -883,7 +883,7 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co $defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], '_default'); } - $defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion); + $defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion, false); $container->setDefinition('assets._default_package', $defaultPackage); $namedPackages = array(); @@ -900,7 +900,7 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co $version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $name); } - $container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version)); + $container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version, $package['is_strict_protocol'])); $namedPackages[$name] = new Reference('assets._package_'.$name); } @@ -913,7 +913,7 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co /** * Returns a definition for an asset package. */ - private function createPackageDefinition($basePath, array $baseUrls, Reference $version) + private function createPackageDefinition($basePath, array $baseUrls, Reference $version, $isStrictProtocol) { if ($basePath && $baseUrls) { throw new \LogicException('An asset package cannot have base URLs and base paths.'); @@ -926,6 +926,10 @@ private function createPackageDefinition($basePath, array $baseUrls, Reference $ ->replaceArgument(1, $version) ; + if ($baseUrls) { + $package->replaceArgument(3, $isStrictProtocol); + } + return $package; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 29d07c5758ee..f2d5b74b4915 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -150,6 +150,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php index dc6bf7bb8df5..e7e653acc37f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php @@ -27,6 +27,10 @@ 'json_manifest_strategy' => array( 'json_manifest_path' => '/path/to/manifest.json', ), + 'strict_protocol' => array( + 'is_strict_protocol' => true, + 'base_urls' => array('http://www.example.com', 'https://www.example.net'), + ), ), ), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml index a907a5b967f9..01dda342178d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml @@ -22,6 +22,10 @@ https://bar_version_strategy.example.com + + http://www.example.com + https://www.example.net + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml index a1679e389ddb..be70de4b30b4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml @@ -19,3 +19,6 @@ framework: version_strategy: assets.custom_version_strategy json_manifest_strategy: json_manifest_path: '/path/to/manifest.json' + strict_protocol: + base_urls: ["http://www.example.com", "https://www.example.net"] + is_strict_protocol: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 4f86c85d4f8f..1965b0a38b09 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -380,23 +380,23 @@ public function testAssets() // default package $defaultPackage = $container->getDefinition((string) $packages->getArgument(0)); - $this->assertUrlPackage($container, $defaultPackage, array('http://cdn.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); + $this->assertUrlPackage($container, $defaultPackage, array('http://cdn.example.com'), 'SomeVersionScheme', '%%s?version=%%s', false); // packages $packages = $packages->getArgument(1); - $this->assertCount(6, $packages); + $this->assertCount(7, $packages); $package = $container->getDefinition((string) $packages['images_path']); $this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s'); $package = $container->getDefinition((string) $packages['images']); - $this->assertUrlPackage($container, $package, array('http://images1.example.com', 'http://images2.example.com'), '1.0.0', '%%s?version=%%s'); + $this->assertUrlPackage($container, $package, array('http://images1.example.com', 'http://images2.example.com'), '1.0.0', '%%s?version=%%s', false); $package = $container->getDefinition((string) $packages['foo']); $this->assertPathPackage($container, $package, '', '1.0.0', '%%s-%%s'); $package = $container->getDefinition((string) $packages['bar']); - $this->assertUrlPackage($container, $package, array('https://bar2.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); + $this->assertUrlPackage($container, $package, array('https://bar2.example.com'), 'SomeVersionScheme', '%%s?version=%%s', false); $package = $container->getDefinition((string) $packages['bar_version_strategy']); $this->assertEquals('assets.custom_version_strategy', (string) $package->getArgument(1)); @@ -405,6 +405,9 @@ public function testAssets() $versionStrategy = $container->getDefinition((string) $package->getArgument(1)); $this->assertEquals('assets.json_manifest_version_strategy', $versionStrategy->getParent()); $this->assertEquals('/path/to/manifest.json', $versionStrategy->getArgument(0)); + + $package = $container->getDefinition((string) $packages['strict_protocol']); + $this->assertUrlPackage($container, $package, array('http://www.example.com', 'https://www.example.net'), 'SomeVersionScheme', '%%s?version=%%s', true); } public function testAssetsDefaultVersionStrategyAsService() @@ -994,10 +997,11 @@ private function assertPathPackage(ContainerBuilder $container, ChildDefinition $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); } - private function assertUrlPackage(ContainerBuilder $container, ChildDefinition $package, $baseUrls, $version, $format) + private function assertUrlPackage(ContainerBuilder $container, ChildDefinition $package, $baseUrls, $version, $format, $isStrictProtocol) { $this->assertEquals('assets.url_package', $package->getParent()); $this->assertEquals($baseUrls, $package->getArgument(0)); + $this->assertEquals($isStrictProtocol, $package->getArgument(3)); $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); } diff --git a/src/Symfony/Component/Asset/Tests/UrlPackageTest.php b/src/Symfony/Component/Asset/Tests/UrlPackageTest.php index 97e7a46d706d..a8a59fd7da65 100644 --- a/src/Symfony/Component/Asset/Tests/UrlPackageTest.php +++ b/src/Symfony/Component/Asset/Tests/UrlPackageTest.php @@ -74,6 +74,37 @@ public function getContextConfigs() array(true, array('http://example.com'), '', 'foo', 'http://example.com/foo?v1'), array(true, array('http://example.com', 'https://example.com'), '', 'foo', 'https://example.com/foo?v1'), + array(true, array('https://example.com', 'https://example.net'), '', 'foo', 'https://example.com/foo?v1'), + array(true, array('https://example.com', 'https://example.net'), '', 'fooa', 'https://example.net/fooa?v1'), + ); + } + + /** + * @dataProvider getStrictUrlConfigs + */ + public function testGetUrlWithStrictUrl($secure, $baseUrls, $format, $path, $expected) + { + $package = new UrlPackage($baseUrls, new StaticVersionStrategy('v1', $format), $this->getContext($secure), true); + + $this->assertEquals($expected, $package->getUrl($path)); + } + + public function getStrictUrlConfigs() + { + return array( + array(false, array('http://example.com', 'http://example.net', 'https://example.com', 'https://example.net'), '', 'foo', 'http://example.com/foo?v1'), + array(false, array('http://example.com', 'http://example.net', 'https://example.com', 'https://example.net'), '', 'fooa', 'http://example.net/fooa?v1'), + + array(true, array('http://example.com', 'http://example.net', 'https://example.com', 'https://example.net'), '', 'foo', 'https://example.com/foo?v1'), + array(true, array('http://example.com', 'http://example.net', 'https://example.com', 'https://example.net'), '', 'fooa', 'https://example.net/fooa?v1'), + + array(false, array('//example.com', 'http://example.com', 'https://example.com'), '', 'foo', '//example.com/foo?v1'), + array(false, array('//example.com', 'http://example.com', 'https://example.com'), '', 'fooa', 'http://example.com/fooa?v1'), + + array(true, array('//example.com', 'http://example.com', 'https://example.com'), '', 'foo', '//example.com/foo?v1'), + array(true, array('//example.com', 'http://example.com', 'https://example.com'), '', 'fooa', 'https://example.com/fooa?v1'), + + array(false, array('https://example.com', 'https://example.net'), '', 'foo', 'https://example.com/foo?v1'), ); } diff --git a/src/Symfony/Component/Asset/UrlPackage.php b/src/Symfony/Component/Asset/UrlPackage.php index 782b2ddfce93..9205eab89f14 100644 --- a/src/Symfony/Component/Asset/UrlPackage.php +++ b/src/Symfony/Component/Asset/UrlPackage.php @@ -27,7 +27,10 @@ * When the request context is available, this package can choose the * best base URL to use based on the current request scheme: * - * * For HTTP request, it chooses between all base URLs; + * * For HTTP request: + * * if $isStrictProtocol is set to false, it chooses between all base URLs + * * if $isStrictProtocol is set to true, it will only use HTTP base URLs and relative protocol URLs + * or falls back to any base URL if no secure ones are available; * * For HTTPs requests, it chooses between HTTPs base URLs and relative protocol URLs * or falls back to any base URL if no secure ones are available. * @@ -35,15 +38,28 @@ */ class UrlPackage extends Package { + /** @var array $baseUrls */ private $baseUrls = array(); - private $sslPackage; + + /** @var string[] $baseSecureUrls */ + private $baseSecureUrls = array(); + + /** @var string[] $baseUnsecureUrls */ + private $baseInsecureUrls = array(); + + /** @var string[] $baseFullUrls */ + private $baseFullUrls = array(); + + /** @var bool $isStrictProtocol */ + private $isStrictProtocol; /** - * @param string|string[] $baseUrls Base asset URLs - * @param VersionStrategyInterface $versionStrategy The version strategy - * @param ContextInterface|null $context Context + * @param string|string[] $baseUrls Base asset URLs + * @param VersionStrategyInterface $versionStrategy The version strategy + * @param ContextInterface|null $context Context + * @param bool $isStrictProtocol Is http strict, or does it allow https on http pages */ - public function __construct($baseUrls, VersionStrategyInterface $versionStrategy, ContextInterface $context = null) + public function __construct($baseUrls, VersionStrategyInterface $versionStrategy, ContextInterface $context = null, $isStrictProtocol = false) { parent::__construct($versionStrategy, $context); @@ -59,11 +75,9 @@ public function __construct($baseUrls, VersionStrategyInterface $versionStrategy $this->baseUrls[] = rtrim($baseUrl, '/'); } - $sslUrls = $this->getSslUrls($baseUrls); + $this->isStrictProtocol = $isStrictProtocol; - if ($sslUrls && $baseUrls !== $sslUrls) { - $this->sslPackage = new self($sslUrls, $versionStrategy); - } + $this->prepareBaseUrl($this->baseUrls); } /** @@ -75,10 +89,6 @@ public function getUrl($path) return $path; } - if (null !== $this->sslPackage && $this->getContext()->isSecure()) { - return $this->sslPackage->getUrl($path); - } - $url = $this->getVersionStrategy()->applyVersion($path); if ($this->isAbsoluteUrl($url)) { @@ -105,6 +115,8 @@ public function getBaseUrl($path) return $this->baseUrls[0]; } + $this->setCurrentBaseUrls(); + return $this->baseUrls[$this->chooseBaseUrl($path)]; } @@ -123,17 +135,44 @@ protected function chooseBaseUrl($path) return (int) fmod(hexdec(substr(hash('sha256', $path), 0, 10)), count($this->baseUrls)); } - private function getSslUrls($urls) + /** + * Set the baseUrls var depending on the context. + */ + private function setCurrentBaseUrls() { - $sslUrls = array(); + if (!empty($this->baseSecureUrls) && $this->getContext()->isSecure()) { + $this->baseUrls = $this->baseSecureUrls; + } elseif (!empty($this->baseInsecureUrls) && $this->isStrictProtocol) { + $this->baseUrls = $this->baseInsecureUrls; + } else { + $this->baseUrls = $this->baseFullUrls; + } + } + + /** + * Split urls in three categories: secure urls, unsecure urls and all urls + * Some url can be found in both secure & insecure categories (// & https depending on $isStrictProtocol option). + * + * @param array $urls + */ + private function prepareBaseUrl(array $urls) + { + $this->baseFullUrls = $urls; + foreach ($urls as $url) { - if ('https://' === substr($url, 0, 8) || '//' === substr($url, 0, 2)) { - $sslUrls[] = $url; - } elseif ('http://' !== substr($url, 0, 7)) { + if ('https://' === substr($url, 0, 8)) { + $this->baseSecureUrls[] = $url; + if ($this->isStrictProtocol === false) { + $this->baseInsecureUrls[] = $url; + } + } elseif ('http://' === substr($url, 0, 7)) { + $this->baseInsecureUrls[] = $url; + } elseif ('//' === substr($url, 0, 2)) { + $this->baseInsecureUrls[] = $url; + $this->baseSecureUrls[] = $url; + } else { throw new InvalidArgumentException(sprintf('"%s" is not a valid URL', $url)); } } - - return $sslUrls; } }