Repository: pallets/werkzeug
Pull request #1584 (2019-06-10 21:03:05)

match server names with default scheme and port
If the configured server name has a port that matches the scheme of the incoming WSGI server name, it will match successfully. Previously, specifying `localhost:443` wouldn't match `https://localhost`.

Discovered while fixing some warnings generated in the Flask test suite after #1577.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index c25dbbdd..8d33e32f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -22,8 +22,11 @@ Unreleased
     "opera" instead of "chrome". :issue:`1556`
 -   The platform for Crosswalk on Android is correctly reported as
     "android" instead of "chromeos". (:pr:`1572`)
--   Issue warning when current server name does not match configured
-    server name. :issue:`760`
+-   Issue a warning when the current server name does not match the
+    configured server name. :issue:`760`
+-   A configured server name with the default port for a scheme will
+    match the current server name without the port if the current scheme
+    matches. :pr:`1584`
 
 
 Version 0.15.5
diff --git a/src/werkzeug/routing.py b/src/werkzeug/routing.py
index c37f73d7..b7b5a066 100644
--- a/src/werkzeug/routing.py
+++ b/src/werkzeug/routing.py
@@ -1485,32 +1485,47 @@ class Map(object):
         :class:`MapAdapter` so that you don't have to pass the path info to
         the match method.
 
-        .. versionchanged:: 0.5
-            previously this method accepted a bogus `calculate_subdomain`
-            parameter that did not have any effect.  It was removed because
-            of that.
+        .. versionchanged:: 1.0.0
+            If the passed server name specifies port 443, it will match
+            if the incoming scheme is ``https`` without a port.
+
+        .. versionchanged:: 1.0.0
+            A warning is shown when the passed server name does not
+            match the incoming WSGI server name.
 
         .. versionchanged:: 0.8
            This will no longer raise a ValueError when an unexpected server
            name was passed.
 
+        .. versionchanged:: 0.5
+            previously this method accepted a bogus `calculate_subdomain`
+            parameter that did not have any effect.  It was removed because
+            of that.
+
         :param environ: a WSGI environment.
         :param server_name: an optional server name hint (see above).
         :param subdomain: optionally the current subdomain (see above).
         """
         environ = _get_environ(environ)
-
         wsgi_server_name = get_host(environ).lower()
+        scheme = environ["wsgi.url_scheme"]
 
         if server_name is None:
             server_name = wsgi_server_name
         else:
             server_name = server_name.lower()
 
+            # strip standard port to match get_host()
+            if scheme == "http" and server_name.endswith(":80"):
+                server_name = server_name[:-3]
+            elif scheme == "https" and server_name.endswith(":443"):
+                server_name = server_name[:-4]
+
         if subdomain is None and not self.host_matching:
             cur_server_name = wsgi_server_name.split(".")
             real_server_name = server_name.split(".")
             offset = -len(real_server_name)
+
             if cur_server_name[offset:] != real_server_name:
                 # This can happen even with valid configs if the server was
                 # accessed directly by IP address under some situations.
@@ -1518,8 +1533,8 @@ class Map(object):
                 # earlier we go by an invalid subdomain which will result
                 # in a 404 error on matching.
                 warnings.warn(
-                    "Current server name '{}' doesn't match configured "
-                    "server name '{}'".format(wsgi_server_name, real_server_name),
+                    "Current server name '{}' doesn't match configured"
+                    " server name '{}'".format(wsgi_server_name, server_name),
                     stacklevel=2,
                 )
                 subdomain = "<invalid>"
@@ -1539,7 +1554,7 @@ class Map(object):
             server_name,
             script_name,
             subdomain,
-            environ["wsgi.url_scheme"],
+            scheme,
             environ["REQUEST_METHOD"],
             path_info,
             query_args=query_args,


Test patch:
diff --git a/tests/test_routing.py b/tests/test_routing.py
index 56a6be6a..07127e8a 100644
--- a/tests/test_routing.py
+++ b/tests/test_routing.py
@@ -368,6 +368,17 @@ def test_invalid_subdomain_warning():
     assert len(record) == 1
 
 
+@pytest.mark.parametrize(
+    ("base", "name"),
+    (("http://localhost", "localhost:80"), ("https://localhost", "localhost:443")),
+)
+def test_server_name_match_default_port(base, name):
+    environ = create_environ("/foo", base_url=base)
+    map = r.Map([r.Rule("/foo", endpoint="foo")])
+    adapter = map.bind_to_environ(environ, server_name=name)
+    assert adapter.match() == ("foo", {})
+
+
 def test_adapter_url_parameter_sorting():
     map = r.Map(
         [r.Rule("/", endpoint="index")], sort_parameters=True, sort_key=lambda x: x[1]