Skip to content

With strict_slashes disabled, leaf paths mask branch paths. #1074

@Parakleta

Description

@Parakleta

When running the following:

from werkzeug.routing import Map, Rule

srv = Map([
    Rule('/path1', endpoint='leaf'),
    Rule('/path1/', endpoint='branch'),
    Rule('/path2', endpoint='leaf', strict_slashes=False),
    Rule('/path2/', endpoint='branch'),
    Rule('/path3', endpoint='leaf'),
    Rule('/path3/', endpoint='branch', strict_slashes=False),
    Rule('/path4', endpoint='leaf', strict_slashes=False),
    Rule('/path4/', endpoint='branch', strict_slashes=False),
], strict_slashes=False).bind('')

print(srv.match('/path1'), srv.match('/path1/'))
print(srv.match('/path2'), srv.match('/path2/'))
print(srv.match('/path3'), srv.match('/path3/'))
print(srv.match('/path4'), srv.match('/path4/'))

the output is:

(('leaf', {}), ('branch', {}))
(('leaf', {}), ('leaf', {}))
(('leaf', {}), ('branch', {}))
(('leaf', {}), ('leaf', {}))

The issue appears to be in werkzeug/routing.py#L745 which allows leaf paths to consume and discard (via werkzeug/routing.py#L776) the trailing slash, and I assume by the route scoring rules the leaf is given priority. This has the effect of silently masking the rule with the trailing slash.

The change that implemented this was in commit 39cbdfd but I'm not sure why it was introduced. It is possible that it was to prevent a KeyError from being thrown by the pop that is now at werkzeug/routing.py#L770 but this can be achieved by just reordering the if statement so that it is if not self.is_leaf and not groups.pop('__suffix__') and .... If this change is made then the following elif ... del can also be removed.

Alternatively if it is intended that turning off strict_slashes will actually make the trailing slash in both the rules and the requests become irrelevant (which is more in line with what the parameter name suggests) then it is unfortunate that it appears the documentation and most of the commentary on the internet is suggesting that this parameter is used to disable the automatic redirection to the trailing slash URL.

I think the current behaviour is a bit confused whatever the intention is. Currently we can have sloppy rule slashes with strict request slashes (no redirection) or strict rule slashes with sloppy request slashes (with redirection). I need strict slashes for both the rules and requests.

If the change from 39cbdfd is reverted then the only capability that would be lost would be when strict_slashes is disabled having /path and /path/ both map to the same endpoint without redirection (/path if it exists, otherwise /path/) but this behaviour can easily be restored by just manually defining both paths to have the same endpoint.

I think having this available would also help provide work-arounds for a range of related issues such as #397 #773 pallets/flask#1783.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions