Register the endpoints list without a trailing slash, and stop hiding its errors
This commit is contained in:
44
tests/test_core/test_no_redirecting_routes.py
Normal file
44
tests/test_core/test_no_redirecting_routes.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""No API route may answer a redirect just because of a trailing slash.
|
||||
|
||||
A rule registered as '/api/thing/' does not merely accept that URL - Werkzeug
|
||||
answers '/api/thing' with a 308 to the slashed form, and the Location it builds
|
||||
is ABSOLUTE. That is fine on the dev server, where scheme and host are what the
|
||||
app thinks they are. It is not fine behind the IIS subpath: with no ProxyFix the
|
||||
app emits http:// and without the /shopdb prefix, the browser refuses to follow
|
||||
an https page to http, and the call fails.
|
||||
|
||||
What made it expensive is where the failure surfaced. The Fleet page caught the
|
||||
error, logged it to the console and rendered "No endpoints found", which reads
|
||||
exactly like a fleet with nothing in it. The agent had been reporting correctly
|
||||
for hours. Every other route in the plugin worked, because every other route is
|
||||
registered without a trailing slash - '/api/endpoints/' was the only one in 331
|
||||
paths, and it was the only thing broken.
|
||||
|
||||
So the rule is: register API routes WITHOUT a trailing slash, or pass
|
||||
strict_slashes=False so both spellings match directly. Either way no client can
|
||||
be handed a redirect it cannot follow.
|
||||
"""
|
||||
|
||||
|
||||
def test_no_api_route_redirects_on_a_trailing_slash(app):
|
||||
# Takes the shared `app` fixture rather than calling create_app itself.
|
||||
# Building a second application inside a test rebinds SQLAlchemy and leaves
|
||||
# the NEXT test module without tables - it passes alone and fails only in a
|
||||
# full run, as "no such table: settings" three modules later. This file
|
||||
# caused exactly that once already.
|
||||
offenders = sorted(
|
||||
rule.rule for rule in app.url_map.iter_rules()
|
||||
if rule.rule.startswith('/api')
|
||||
and rule.rule.endswith('/')
|
||||
and rule.rule != '/api/'
|
||||
and rule.strict_slashes
|
||||
)
|
||||
|
||||
assert not offenders, (
|
||||
'%d API route(s) are registered with a trailing slash AND strict '
|
||||
'slashes, so Flask answers the unslashed URL with a 308 to an absolute '
|
||||
'URL. Behind a reverse proxy that redirect is unfollowable and the call '
|
||||
'fails, usually looking like an empty list rather than an error.\n'
|
||||
'Register the route without the trailing slash, or pass '
|
||||
'strict_slashes=False:\n %s'
|
||||
% (len(offenders), '\n '.join(offenders)))
|
||||
Reference in New Issue
Block a user