fix(app-root): restore legacy redirects for HEAD and the /superset app root#42015
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #42015 +/- ##
==========================================
- Coverage 65.06% 65.05% -0.01%
==========================================
Files 2747 2747
Lines 153806 153842 +36
Branches 35266 35270 +4
==========================================
+ Hits 100072 100088 +16
- Misses 51824 51845 +21
+ Partials 1910 1909 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…p root The legacy-prefix redirect shim had two gaps that broke it for real deployments. HEAD requests returned 410 Gone on every legacy route. LEGACY_REDIRECT_MAP lists only the methods each canonical @expose decorator names explicitly, which never includes HEAD (Werkzeug registers it implicitly on GET rules). The method check compared the raw request method against that map, so every HEAD probe — link-checkers, uptime monitors, `curl -I` — fell into the wrong-method branch. Fold HEAD to GET before the check, so it tracks GET without blanket-allowing it: HEAD against the one POST-only canonical still returns 410. Deployments with APPLICATION_ROOT=/superset disabled the shim entirely. The guard assumed that when the app-root and legacy prefixes are the same token, any redirect would self-loop. It cannot: the app-root strip runs before the legacy-prefix check, so a canonical URL never re-enters the redirect branch. A legacy bookmark in that deployment carries the prefix twice (/superset/superset/dashboard/1/) and hard-404'd instead of 308ing to /superset/dashboard/1/. Remove the guard, and add a precise per-request check that passes through rather than emitting a redirect whose target equals the inbound path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
648c28a to
6dcdc65
Compare
Code Review Agent Run #48fbf3Actionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
SUMMARY
Two bugs in the legacy
/superset/*→ canonical route redirect shim(
LegacyPrefixRedirectMiddleware), both of which break real deployments.1.
HEADagainst any legacy route returns410 Goneinstead of redirecting.LEGACY_REDIRECT_MAPspells only the methods each canonical route's@exposedecorator names explicitly, which never includes
HEAD— Werkzeug registersHEADimplicitly on everyGETrule, so no route declares it. The middlewarecompared the raw request method against that map, so every legacy
HEADrequestfell into the wrong-method branch and got
410 Gone. That breaks link-checkers,uptime monitors, and plain
curl -Iagainst any legacy URL.HEADisGET-without-a-body (RFC 9110 §9.3.2), so it is now folded toGETbefore the method check. It tracks
GETrather than being blanket-allowed:HEADagainst the onePOST-only canonical (/log/) still correctly returns410.2. The shim is disabled entirely when
APPLICATION_ROOTis/superset.An
_enabledguard turned the middleware off in that deployment, on the premisethat the legacy prefix and the app-root prefix being the same token would produce
an infinite 308 loop.
That premise was incorrect. The app-root strip runs before the legacy-prefix
check, so a canonical URL can never re-enter the redirect branch: inbound
/superset/welcome/strips to/welcome/, which is not a legacy path, and passesthrough untouched. No redirect, no loop. The guard's only actual effect was to
make legacy bookmarks under that root — which arrive carrying the prefix twice,
/superset/superset/dashboard/1/— hard-404 rather than 308 to/superset/dashboard/1/.The guard is removed. In its place is a precise per-request check: if the computed
redirect target is byte-identical to the inbound path, pass through instead of
emitting a redirect that could not converge. No enumerated row can produce that
today (no canonical target begins with the legacy prefix, so the rewrite always
shortens the path) — it's a guard against a future map edit, not a live condition.
TESTING INSTRUCTIONS
pytest tests/unit_tests/middleware/test_legacy_prefix_redirect.py— 206 pass.Both fixes were written test-first; the 58 new tests fail against the previous
middleware and pass against this one.
GET-redirecting row overHEAD,with and without an app-root prefix, plus the SQL Lab deep-link special case and
a
/log/control assertingHEADagainst aPOST-only canonical still410s.APPLICATION_ROOT=/superseta doubledlegacy path 308s to the canonical one, a canonical path passes straight through
(the no-loop invariant), and the bare app root is untouched. Four existing tests
that pinned the buggy
_enabledbehavior were rewritten to pin the fixedbehavior.
Manual: deploy with
SUPERSET_APP_ROOT=/supersetand confirmcurl -I <host>/superset/superset/dashboard/1/returns308withLocation: /superset/dashboard/1/(both bugs at once — it was410before,and
404on the followed target).ADDITIONAL INFORMATION