diff --git a/CHANGES.txt b/CHANGES.txt index c7f32ea4..5966bf8c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,13 @@ Unreleased Bugfix ~~~~~~ +- A non-ASCII ``url_prefix`` configuration value is now converted to the same + "UTF-8 bytes decoded as latin-1" representation WSGI's PATH_INFO/SCRIPT_NAME + use for the actual request path, so it can now correctly match incoming + requests. Previously, a non-ASCII url_prefix could never match any request + path at all. See + https://github.com/Pylons/waitress/issues/492 + - Renamed the HTTP header "Trailers" to "Trailer" to fix a typo and comply with the correct header name as specified in RFC 7230. diff --git a/src/waitress/adjustments.py b/src/waitress/adjustments.py index 8b26eb87..65a84a2b 100644 --- a/src/waitress/adjustments.py +++ b/src/waitress/adjustments.py @@ -75,6 +75,17 @@ def slash_fixed_str(s): # always have a leading slash, replace any number of leading slashes # with a single slash, and strip any trailing slashes s = "/" + s.lstrip("/").rstrip("/") + # Per PEP 3333, WSGI's PATH_INFO/SCRIPT_NAME are represented as + # "native strings" holding raw request bytes decoded as latin-1 + # (i.e. each byte becomes the code point of the same value), + # regardless of the request's actual encoding. The configured + # url_prefix, in contrast, is an ordinary Unicode string as the + # user wrote it. Encoding it the same way here (UTF-8 bytes, + # then decoded as latin-1) ensures comparisons against the + # request path (see HTTPTask.execute()) use the same + # convention on both sides, so a non-ASCII url_prefix can + # actually match. See GH #492. + s = s.encode("utf-8").decode("latin-1") return s diff --git a/tests/test_adjustments.py b/tests/test_adjustments.py index 86bf5ded..379c7953 100644 --- a/tests/test_adjustments.py +++ b/tests/test_adjustments.py @@ -174,6 +174,29 @@ def test_goodvars(self): # localhost... self.assertTupleEqual(("127.0.0.1", 8080), bind_pairs[0]) + def test_url_prefix_non_ascii_matches_wsgi_encoded_path(self): + """ + Regression test for + https://github.com/Pylons/waitress/issues/492 + + A non-ASCII url_prefix must be converted to the same + "utf8-as-latin1" representation WSGI's PATH_INFO/SCRIPT_NAME + use for the actual request path (see HTTPTask.execute() in + task.py), or a non-ASCII url_prefix can never match an + incoming request's path. + """ + inst = self._makeOne(url_prefix="/日本語") + + # What the request path would actually look like once the raw + # UTF-8 bytes sent by a client are decoded as latin-1, per + # PEP 3333's WSGI string convention. + expected = "/日本語".encode("utf-8").decode("latin-1") + self.assertEqual(inst.url_prefix, expected) + + def test_url_prefix_ascii_is_unaffected(self): + inst = self._makeOne(url_prefix="///api/v1///") + self.assertEqual(inst.url_prefix, "/api/v1") + def test_goodvar_listen(self): inst = self._makeOne(listen="127.0.0.1")