Repository: pallets/werkzeug

Issue:
Path matching with URLs ending in //
<!-- **This issue tracker is a tool to address bugs in Flask itself.
Please use the #pocoo IRC channel on freenode or Stack Overflow for general
questions about using Flask or issues not related to Flask.** -->

<!-- If you'd like to report a bug in Flask, fill out the template below. Provide
any extra information that may be useful / related to your problem.
Ideally, create an [MCVE](https://stackoverflow.com/help/mcve), which helps us
understand the problem and helps check that it is not caused by something in
your code. -->

I can't seem to get flask to match URLs ending in double slashes. I have a client app (that I don't control) that under some circumstances sends requests like this: `/v1/collections//`
 
### Expected Behavior

Output "found double"

```python
from flask import Flask
app = Flask(__name__)

@app.route('/test//')
def double(other):
    return 'found double'
```

### Actual Behavior

```
curl localhost:5000/test//
# 404 not found
```

I have also tried with paths:

```
@app.route('/test/<path:other>')
def double(other):
    return 'found double'
```

but no luck either.

### Environment

```
$ flask --version
Python 3.7.4
Flask 1.1.1
Werkzeug 0.16.0
```



Discussion:
I can't seem to find a way, even with a catch-all, to hit a double slashed ended uri. Seems like you typically need a rewrite rule to get rid of the trailing slashes. One solution would be to put an nginx reverse proxy in front of the flask app with a rewrite in the config. e.g. `rewrite ^/test/(.*)$ /$1 break;` or `^test(/.*)$`
Wouldn't you consider this a bug? After all I want my route to match a certain pattern and something is preventing that. At the least I think it is unexpected behaviour.

Especially since internal double slashes work (`@app.route('/test//hase')`)

Rewriting in an upstream proxy is some pain. Then my development environment behaves differently and I cannot unit-test those particular routes. 

If there's really no way to match that, can I somehow rewrite the URL in flask before the path matching happens? 

Thanks,
Heinz
try this
```
from flask import Flask
from werkzeug.routing import PathConverter

app = Flask(__name__)


class EverythingConverter(PathConverter):
    regex = '.*?'


app.url_map.converters['everything'] = EverythingConverter


@app.route('/test/<everything:other>')
def double(other):
    print(f'other: {other}')
    return 'found double'
```

https://stackoverflow.com/questions/24000729/flask-route-using-path-with-leading-slash

Resolving patch:
diff --git a/src/werkzeug/routing.py b/src/werkzeug/routing.py
--- a/src/werkzeug/routing.py
+++ b/src/werkzeug/routing.py
@@ -820,12 +820,14 @@ def _build_regex(rule):
 
         if self.build_only:
             return
-        regex = r"^%s%s$" % (
-            u"".join(regex_parts),
-            (not self.is_leaf or not self.strict_slashes)
-            and "(?<!/)(?P<__suffix__>/?)"
-            or "",
-        )
+
+        if not (self.is_leaf and self.strict_slashes):
+            reps = u"*" if self.merge_slashes else u"?"
+            tail = u"(?<!/)(?P<__suffix__>/%s)" % reps
+        else:
+            tail = u""
+
+        regex = u"^%s%s$" % (u"".join(regex_parts), tail)
         self._regex = re.compile(regex, re.UNICODE)
 
     def match(self, path, method=None):


Test patch:
diff --git a/tests/test_routing.py b/tests/test_routing.py
--- a/tests/test_routing.py
+++ b/tests/test_routing.py
@@ -83,6 +83,11 @@ def test_merge_slashes_match():
 
     assert excinfo.value.new_url.endswith("/yes/tail/")
 
+    with pytest.raises(r.RequestRedirect) as excinfo:
+        adapter.match("/yes/tail//")
+
+    assert excinfo.value.new_url.endswith("/yes/tail/")
+
     assert adapter.match("/no/tail")[0] == "no_tail"
     assert adapter.match("/yes/tail/")[0] == "yes_tail"