Skip to content

Fix the servlet encodeURL methods and the root context path - #16564

Merged
EarthChen merged 2 commits into
apache:3.3from
CCCLL:servlet-redirect-location
Oct 30, 2026
Merged

EarthChen merged 2 commits into
apache:3.3from
CCCLL:servlet-redirect-location

Conversation

@CCCLL

@CCCLL CCCLL commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

What is the purpose of the change?

Fixes #16563.

Servlet code running in Triple REST (dubbo-triple-servlet) sent wrong Location headers when it redirected the usual ways:

  • ServletHttpRequestAdapter.getContextPath() and DummyServletContext.getContextPath() returned "/", so sendRedirect(getContextPath() + "/login") sent //login. The servlet API returns "" for the root context. They now return "".
  • ServletHttpResponseAdapter.encodeURL, encodeRedirectURL, encodeUrl and encodeRedirectUrl returned URLEncoder.encode(url), so sendRedirect(encodeRedirectURL("/login?next=/orders")) sent %2Flogin%3Fnext%3D%2Forders. In the servlet API these methods only add a session id for URL rewriting and otherwise return the URL unchanged. Dubbo does not rewrite URLs for sessions, so they now return the URL as it is.

Both are needed for code like Spring Security's DefaultRedirectStrategy, which calls encodeRedirectURL(getContextPath() + url): with only the second change it would send //login?next=/orders. The PR has one commit for each, the context path first.

Behavior change

  • encodeURL, encodeRedirectURL, encodeUrl and encodeRedirectUrl return their argument unchanged instead of a form-encoded copy.
  • HttpServletRequest#getContextPath() and ServletContext#getContextPath() return "" instead of "/".
  • Code that used encodeURL to percent-escape a value now gets the value back unescaped, as with any servlet container.
  • The generated jakarta adapters change the same way, since they are copied from these sources at build time.

Not covered

  • A non-root context: when the protocol has a contextpath, or TripleFilter runs in a webapp that is not deployed at the root, getContextPath() now returns "" instead of "/" (a redirect to getContextPath() + "/login" goes to /login instead of //login), but a servlet container would return that context path and leave it out of getServletPath(). Doing that needs the context path of the matched service or of the container request, so I left it out of this PR.

Verification

  • New ServletHttpRequestAdapterTest (empty context path for the request and the servlet context) and ServletHttpResponseAdapterTest (the four methods return /login?next=/orders, http://example.com/a b?x=1#y and "" unchanged). They fail before the change (expected: <> but was: </>, expected: </login?next=/orders> but was: <%2Flogin%3Fnext%3D%2Forders>) and pass after it. Putting the old code back into any one of the six methods makes a test fail. The tests use the javax classes because the jakarta copies are only generated on JDK 17+.
  • mvn clean test verify of dubbo-triple-servlet and dubbo-rest-spring with the options of the CI unit test job, on JDK 8, 11, 17, 21 and 25: 61 tests, 0 failures each time (11 skipped, the existing @Disabled SpringMvcRestProtocolTest); checkstyle and spotless are clean.
  • End to end, the three redirects from the issue, compared with the same servlet code in Tomcat 9.0.121 (Spring Security 5.8.16 for /security):
Request 3.3.6 this PR Tomcat 9.0.121
/redirect %2Flogin%3Fnext%3D%2Forders /login?next=/orders /login?next=/orders
/context //login?next=/orders /login?next=/orders /login?next=/orders
/security %2F%2Flogin%3Fnext%3D%2Forders /login?next=/orders /login?next=/orders

Checklist

  • Make sure there is a GitHub_issue field for the change.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit-test to verify your logic correction. If the new feature or significant change is committed, please remember to add sample in dubbo samples project.
  • Make sure gitHub actions can pass. Why the workflow is failing and how to fix it?

CCCLL added 2 commits October 30, 2026 12:30
ServletHttpRequestAdapter.getContextPath() and
DummyServletContext.getContextPath() returned "/", but the servlet API
returns "" for the root context. Code that builds a URL from the
context path, such as
response.sendRedirect(request.getContextPath() + "/login") or Spring
Security's DefaultRedirectStrategy, got "//login" instead of "/login".

Fixes apache#16563
ServletHttpResponseAdapter implemented encodeURL, encodeRedirectURL,
encodeUrl and encodeRedirectUrl with URLEncoder, so the whole URL was
form-encoded and a redirect sent through encodeRedirectURL got a
Location such as %2Flogin%3Fnext%3D%2Forders. These methods only add
a session id when URL rewriting is used, and otherwise return the URL
unchanged. Dubbo does not rewrite URLs for sessions, so return the
URL as it is.

Fixes apache#16563
@codecov-commenter

codecov-commenter commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.98%. Comparing base (1afcdf7) to head (984d823).

Additional details and impacted files
@@             Coverage Diff              @@
##                3.3   #16564      +/-   ##
============================================
+ Coverage     59.02%   60.98%   +1.95%     
  Complexity       15       15              
============================================
  Files          1953     1953              
  Lines         89284    89284              
  Branches      13477    13477              
============================================
+ Hits          52700    54449    +1749     
+ Misses        30877    29242    -1635     
+ Partials       5707     5593     -114     
Flag Coverage Δ
integration-tests-java21 32.13% <0.00%> (?)
integration-tests-java8 32.20% <0.00%> (?)
samples-tests-java21 32.13% <0.00%> (?)
samples-tests-java8 29.78% <0.00%> (?)
unit-tests-java11 59.21% <100.00%> (+<0.01%) ⬆️
unit-tests-java17 58.69% <100.00%> (+0.03%) ⬆️
unit-tests-java21 58.71% <100.00%> (+0.02%) ⬆️
unit-tests-java25 58.65% <100.00%> (-0.02%) ⬇️
unit-tests-java8 59.24% <100.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@EarthChen EarthChen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally on 3.3 (merge-base 1afcdf7883, PR head 984d823420).

Reproduction. With the three main files reverted to the base, the new tests fail: getContextPath() returns / instead of an empty string, encodeRedirectURL("/login?next=/orders") returns %2Flogin%3Fnext%3D%2Forders, and encodeURL encodes the whole URL. On the PR head both test classes pass (1 + 3 tests), and Spotless and checkstyle pass for the module.

Contract. The fix matches the Servlet API: getContextPath() returns an empty string for the root context, and encodeURL / encodeRedirectURL return the URL unchanged when no session id is added. Dubbo does not rewrite URLs for sessions.

Side effects. I found no reader of these getContextPath() implementations. Routing uses the container's getRequestURI(). RequestUtils.encodeURL is still used by the JAX-RS adapter and tests, and the PR removed the now-unused import.

Minor, non-blocking: the empty-string case in ServletHttpResponseAdapterTest also passes on the old code, and no test covers sendRedirect end to end. A non-root context path is still unsupported, as the issue notes.

@EarthChen
EarthChen merged commit 7ca3e29 into apache:3.3 Oct 30, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Redirects from servlet code in Triple REST get a wrong Location: encodeRedirectURL encodes the whole URL and getContextPath() returns "/"

3 participants