Skip to content

[Routing] support scheme requirement without redirectable dumped matcher #26304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 28, 2018

Conversation

Tobion
Copy link
Contributor

@Tobion Tobion commented Feb 25, 2018

Q A
Branch? master
Bug fix? yes
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

The scheme handling was just redirecting immediately without testing the other routes at all for potential matches. This can cause Problems when you try to have different routes/controllers for the same path but different schemes. See added test.

        $coll = new RouteCollection();
        $coll->add('https_route', new Route('/', array(), array(), array(), '', array('https')));
        $coll->add('http_route', new Route('/', array(), array(), array(), '', array('http')));
        $matcher = $this->getUrlMatcher($coll);
        $this->assertEquals(array('_route' => 'http_route'), $matcher->match('/'));

Instead of matching the right route, it would redirect immediatly as soon as it hits the first route.
This does not make sense and is not consistent with the other logic. Redirection should only happen when nothing matches. While fixing this I could also remove the limitation

throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');

If redirection is not possible, the wrong scheme will just not match the route. This is the same as already implemented by UrlMatcher (not RedirecableUrlMatcher). If redirection is supported, it will redirect to the first supported scheme if no other route matches. This makes the implementation similar to redirection for trailing slash and handling not allowed methods.

Also previously, the scheme redirection was done for non-safe verbs which shouldn't happen as well, ref. #25962

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the $supportsRedirections property should be turned back to a local variable


return \$this->redirect(\$rawPathinfo, \$ret['_route'], key(\$requiredSchemes)) + \$ret;
if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
\$allow += \$requiredMethods;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By checking requirements before methods, we fixed an issue where a 405 is triggered while a scheme requirement is not met. Shouldn't we keep this fixed behavior, ie check methods last?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought about this alot. There are cases were it's not optimal in both cases.

Request GET http /
Route POST https /

If you check the scheme first instead of the method. It will try to make a redirect from http to https although the route requires POST which is not met.

So I think checking method first is better as getting a 405 instead of a 404 for the case you explained does not really matter. Also this order is what was implemented by UrlMatcher. So it makes it more consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is why I was talking about that: report in #22739, fix just sent in #26312.

$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
if ($ret = $this->doMatch($pathinfo)) {
return $this->redirect($pathinfo, $ret['_route']) + $ret;
if (in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\in_array can be used

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file is dumped in the root namespace

@Tobion
Copy link
Contributor Author

Tobion commented Feb 25, 2018

the $supportsRedirections property should be turned back to a local variable

We would still need to pass the var to generateMatchMethod. As a property it's more flexible IMO.

@nicolas-grekas nicolas-grekas added this to the 4.1 milestone Feb 26, 2018
@nicolas-grekas
Copy link
Member

ready for rebase

@Tobion Tobion force-pushed the simplify-scheme-redirect branch 2 times, most recently from 91ecd89 to cbbc3e2 Compare February 26, 2018 22:22
@Tobion
Copy link
Contributor Author

Tobion commented Feb 26, 2018

Rebased

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with minor comment


if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
\$allow += \$requiredMethods;
if (\$requiredSchemes && !\$hasRequiredScheme) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!\$hasRequiredScheme) { would be enough here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -53,7 +53,7 @@ public function testSchemeRedirect()
'scheme' => 'https',
'httpPort' => $context->getHttpPort(),
'httpsPort' => $context->getHttpsPort(),
'_route' => 'foo',
'_route' => null,
Copy link
Member

@nicolas-grekas nicolas-grekas Feb 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change breaks the 3.4 test suite, can't we do without?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know the route that would match, unless we do a new match like for trailing slash. Also the way it worked was not reliable anyway. When you have a condition that didn't match because of the scheme but now matches with a different scheme, this route can be before the route that caused the redirect. So the actual route that will match might not be the one that was passed here at all.

Copy link
Member

@nicolas-grekas nicolas-grekas Feb 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we still need to make the currently failing test green before merging...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed :)

@Tobion Tobion force-pushed the simplify-scheme-redirect branch 2 times, most recently from 735bc1d to 7154ec1 Compare February 27, 2018 14:37
@nicolas-grekas nicolas-grekas force-pushed the simplify-scheme-redirect branch from 7154ec1 to 8c36250 Compare February 28, 2018 07:56
@nicolas-grekas nicolas-grekas force-pushed the simplify-scheme-redirect branch 3 times, most recently from beec4f7 to ddb5be6 Compare February 28, 2018 09:18
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Feb 28, 2018

@Tobion scheme redirection now does a match before, similar to trailing slash redirection. I also added another redirection, doing a trailing slash + scheme redirection when possible.

@nicolas-grekas nicolas-grekas force-pushed the simplify-scheme-redirect branch 6 times, most recently from 8b3bc06 to afc6254 Compare February 28, 2018 09:51
$this->allow = array_merge($this->allow, $requiredMethods);
}

continue;
}
}

return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
$ret = $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reason for doing that before the $hasRequiredScheme check?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted, was a leftover from previous iteration

@nicolas-grekas nicolas-grekas force-pushed the simplify-scheme-redirect branch from afc6254 to 2e27658 Compare February 28, 2018 10:50
@Tobion
Copy link
Contributor Author

Tobion commented Feb 28, 2018

Please add a test for scheme + trailing slash redirect. Then good to merge.

@nicolas-grekas nicolas-grekas force-pushed the simplify-scheme-redirect branch from 2e27658 to f9b54c5 Compare February 28, 2018 11:35
@nicolas-grekas
Copy link
Member

Thank you @Tobion.

@nicolas-grekas nicolas-grekas merged commit f9b54c5 into symfony:master Feb 28, 2018
nicolas-grekas added a commit that referenced this pull request Feb 28, 2018
…ble dumped matcher (Tobion)

This PR was merged into the 4.1-dev branch.

Discussion
----------

[Routing] support scheme requirement without redirectable dumped matcher

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no <!-- don't forget to update UPGRADE-*.md files -->
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets |
| License       | MIT
| Doc PR        |

The scheme handling was just redirecting immediately without testing the other routes at all for potential matches. This can cause Problems when you try to have different routes/controllers for the same path but different schemes. See added test.

```
        $coll = new RouteCollection();
        $coll->add('https_route', new Route('/', array(), array(), array(), '', array('https')));
        $coll->add('http_route', new Route('/', array(), array(), array(), '', array('http')));
        $matcher = $this->getUrlMatcher($coll);
        $this->assertEquals(array('_route' => 'http_route'), $matcher->match('/'));
```

Instead of matching the right route, it would redirect immediatly as soon as it hits the first route.
This does not make sense and is not consistent with the other logic. Redirection should only happen when nothing matches. While fixing this I could also remove the limitation

> throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');

If redirection is not possible, the wrong scheme will just not match the route. This is the same as already implemented by UrlMatcher (not RedirecableUrlMatcher). If redirection is supported, it will redirect to the first supported scheme if no other route matches. This makes the implementation similar to redirection for trailing slash and handling not allowed methods.

Also previously, the scheme redirection was done for non-safe verbs which shouldn't happen as well, ref. #25962

Commits
-------

f9b54c5 [Routing] support scheme requirement without redirectable dumped matcher
@Tobion Tobion deleted the simplify-scheme-redirect branch February 28, 2018 12:06
@fabpot fabpot mentioned this pull request May 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants