bug #32000 [Routing] fix absolute url generation when scheme is not known (Tobion)

This PR was merged into the 3.4 branch.

Discussion
----------

[Routing] fix absolute url generation when scheme is not known

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | #25491
| License       | MIT
| Doc PR        |

This fixes two edge cases in the url generator:
1. when the context scheme is not known (empty) generating an absolute url would return an invalid url starting with `://host/path`. #25491 handled the case when the host is unknown which makes sense. but the way it was done, created this new problem.
2. non-http(s) urls do not require a host. e.g. typical `file:///path` urls. url generator is fixed to be in line with rfc3986

Commits
-------

8e04222976 [Routing] fix absolute url generation when scheme is not known
This commit is contained in:
Fabien Potencier 2019-06-13 18:44:13 +02:00
commit bd9d0a4793
2 changed files with 51 additions and 17 deletions

View File

@ -222,16 +222,18 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
}
}
if ((self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) && !empty($host)) {
$port = '';
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
$port = ':'.$this->context->getHttpPort();
} elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
$port = ':'.$this->context->getHttpsPort();
}
if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) {
if ('' !== $host || ('' !== $scheme && 'http' !== $scheme && 'https' !== $scheme)) {
$port = '';
if ('http' === $scheme && 80 !== $this->context->getHttpPort()) {
$port = ':'.$this->context->getHttpPort();
} elseif ('https' === $scheme && 443 !== $this->context->getHttpsPort()) {
$port = ':'.$this->context->getHttpsPort();
}
$schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://";
$schemeAuthority .= $host.$port;
$schemeAuthority = self::NETWORK_PATH === $referenceType || '' === $scheme ? '//' : "$scheme://";
$schemeAuthority .= $host.$port;
}
}
if (self::RELATIVE_PATH === $referenceType) {

View File

@ -480,28 +480,27 @@ class UrlGeneratorTest extends TestCase
public function testDefaultHostIsUsedWhenContextHostIsEmpty()
{
$routes = $this->getRoutes('test', new Route('/route', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}', ['http']));
$routes = $this->getRoutes('test', new Route('/path', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}'));
$generator = $this->getGenerator($routes);
$generator->getContext()->setHost('');
$this->assertSame('http://my.fallback.host/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
$this->assertSame('http://my.fallback.host/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
}
public function testDefaultHostIsUsedWhenContextHostIsEmptyAndSchemeIsNot()
public function testDefaultHostIsUsedWhenContextHostIsEmptyAndPathReferenceType()
{
$routes = $this->getRoutes('test', new Route('/route', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}', ['http', 'https']));
$routes = $this->getRoutes('test', new Route('/path', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}'));
$generator = $this->getGenerator($routes);
$generator->getContext()->setHost('');
$generator->getContext()->setScheme('https');
$this->assertSame('https://my.fallback.host/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
$this->assertSame('//my.fallback.host/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH));
}
public function testAbsoluteUrlFallbackToRelativeIfHostIsEmptyAndSchemeIsNot()
public function testAbsoluteUrlFallbackToPathIfHostIsEmptyAndSchemeIsHttp()
{
$routes = $this->getRoutes('test', new Route('/route', [], [], [], '', ['http', 'https']));
$routes = $this->getRoutes('test', new Route('/route'));
$generator = $this->getGenerator($routes);
$generator->getContext()->setHost('');
@ -510,6 +509,39 @@ class UrlGeneratorTest extends TestCase
$this->assertSame('/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
}
public function testAbsoluteUrlFallbackToNetworkIfSchemeIsEmptyAndHostIsNot()
{
$routes = $this->getRoutes('test', new Route('/path'));
$generator = $this->getGenerator($routes);
$generator->getContext()->setHost('example.com');
$generator->getContext()->setScheme('');
$this->assertSame('//example.com/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
}
public function testAbsoluteUrlFallbackToPathIfSchemeAndHostAreEmpty()
{
$routes = $this->getRoutes('test', new Route('/path'));
$generator = $this->getGenerator($routes);
$generator->getContext()->setHost('');
$generator->getContext()->setScheme('');
$this->assertSame('/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
}
public function testAbsoluteUrlWithNonHttpSchemeAndEmptyHost()
{
$routes = $this->getRoutes('test', new Route('/path', [], [], [], '', ['file']));
$generator = $this->getGenerator($routes);
$generator->getContext()->setBaseUrl('');
$generator->getContext()->setHost('');
$this->assertSame('file:///path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
}
public function testGenerateNetworkPath()
{
$routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com', ['http']));