Skip to content

Commit 986d219

Browse files
committed
Replaces rather than prepend contextPath
Issue: SPR-16650
1 parent de4da5e commit 986d219

File tree

2 files changed

+71
-18
lines changed

2 files changed

+71
-18
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,15 @@ protected ServletUriComponentsBuilder(ServletUriComponentsBuilder other) {
8181
*
8282
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
8383
* and "X-Forwarded-*" headers if found. See class-level docs.
84+
*
85+
* <p>As of 4.3.15, this method replaces the contextPath with the value
86+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
87+
* {@code ForwardedHeaderFiller}.
8488
*/
8589
public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest request) {
8690
ServletUriComponentsBuilder builder = initFromRequest(request);
87-
builder.replacePath(prependForwardedPrefix(request, request.getContextPath()));
91+
String forwardedPrefix = getForwardedPrefix(request);
92+
builder.replacePath(forwardedPrefix != null ? forwardedPrefix : request.getContextPath());
8893
return builder;
8994
}
9095

@@ -98,6 +103,10 @@ public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest req
98103
*
99104
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
100105
* and "X-Forwarded-*" headers if found. See class-level docs.
106+
*
107+
* <p>As of 4.3.15, this method replaces the contextPath with the value
108+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
109+
* {@code ForwardedHeaderFiller}.
101110
*/
102111
public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest request) {
103112
ServletUriComponentsBuilder builder = fromContextPath(request);
@@ -113,10 +122,14 @@ public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest
113122
*
114123
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
115124
* and "X-Forwarded-*" headers if found. See class-level docs.
125+
*
126+
* <p>As of 4.3.15, this method replaces the contextPath with the value
127+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
128+
* {@code ForwardedHeaderFiller}.
116129
*/
117130
public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest request) {
118131
ServletUriComponentsBuilder builder = initFromRequest(request);
119-
builder.initPath(prependForwardedPrefix(request, request.getRequestURI()));
132+
builder.initPath(getRequestUriWithForwardedPrefix(request));
120133
return builder;
121134
}
122135

@@ -126,10 +139,14 @@ public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest requ
126139
*
127140
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
128141
* and "X-Forwarded-*" headers if found. See class-level docs.
142+
*
143+
* <p>As of 4.3.15, this method replaces the contextPath with the value
144+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
145+
* {@code ForwardedHeaderFiller}.
129146
*/
130147
public static ServletUriComponentsBuilder fromRequest(HttpServletRequest request) {
131148
ServletUriComponentsBuilder builder = initFromRequest(request);
132-
builder.initPath(prependForwardedPrefix(request, request.getRequestURI()));
149+
builder.initPath(getRequestUriWithForwardedPrefix(request));
133150
builder.query(request.getQueryString());
134151
return builder;
135152
}
@@ -153,7 +170,7 @@ private static ServletUriComponentsBuilder initFromRequest(HttpServletRequest re
153170
return builder;
154171
}
155172

156-
private static String prependForwardedPrefix(HttpServletRequest request, String path) {
173+
private static String getForwardedPrefix(HttpServletRequest request) {
157174
String prefix = null;
158175
Enumeration<String> names = request.getHeaderNames();
159176
while (names.hasMoreElements()) {
@@ -163,7 +180,22 @@ private static String prependForwardedPrefix(HttpServletRequest request, String
163180
}
164181
}
165182
if (prefix != null) {
166-
path = prefix + path;
183+
while (prefix.endsWith("/")) {
184+
prefix = prefix.substring(0, prefix.length() - 1);
185+
}
186+
}
187+
return prefix;
188+
}
189+
190+
private static String getRequestUriWithForwardedPrefix(HttpServletRequest request) {
191+
String path = request.getRequestURI();
192+
String forwardedPrefix = getForwardedPrefix(request);
193+
if (forwardedPrefix != null) {
194+
String contextPath = request.getContextPath();
195+
if (!StringUtils.isEmpty(contextPath) && !contextPath.equals("/") && path.startsWith(contextPath)) {
196+
path = path.substring(contextPath.length());
197+
}
198+
path = forwardedPrefix + path;
167199
}
168200
return path;
169201
}
@@ -177,6 +209,10 @@ private static String prependForwardedPrefix(HttpServletRequest request, String
177209
*
178210
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
179211
* and "X-Forwarded-*" headers if found. See class-level docs.
212+
*
213+
* <p>As of 4.3.15, this method replaces the contextPath with the value
214+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
215+
* {@code ForwardedHeaderFiller}.
180216
*/
181217
public static ServletUriComponentsBuilder fromCurrentContextPath() {
182218
return fromContextPath(getCurrentRequest());
@@ -199,6 +235,10 @@ public static ServletUriComponentsBuilder fromCurrentServletMapping() {
199235
*
200236
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
201237
* and "X-Forwarded-*" headers if found. See class-level docs.
238+
*
239+
* <p>As of 4.3.15, this method replaces the contextPath with the value
240+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
241+
* {@code ForwardedHeaderFiller}.
202242
*/
203243
public static ServletUriComponentsBuilder fromCurrentRequestUri() {
204244
return fromRequestUri(getCurrentRequest());
@@ -210,6 +250,10 @@ public static ServletUriComponentsBuilder fromCurrentRequestUri() {
210250
*
211251
* <p><strong>Note:</strong> This method extracts values from "Forwarded"
212252
* and "X-Forwarded-*" headers if found. See class-level docs.
253+
*
254+
* <p>As of 4.3.15, this method replaces the contextPath with the value
255+
* of "X-Forwarded-Prefix" rather than prepending, thus aligning with
256+
* {@code ForwardedHeaderFiller}.
213257
*/
214258
public static ServletUriComponentsBuilder fromCurrentRequest() {
215259
return fromRequest(getCurrentRequest());

spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,33 @@ public void fromRequestUri() {
102102
assertEquals("http://localhost/mvc-showcase/data/param", result);
103103
}
104104

105-
@Test
105+
@Test // SPR-16650
106106
public void fromRequestWithForwardedPrefix() {
107-
this.request.setRequestURI("/bar");
108-
this.request.addHeader("X-Forwarded-Prefix", "/foo");
107+
this.request.addHeader("X-Forwarded-Prefix", "/prefix");
108+
this.request.setContextPath("/mvc-showcase");
109+
this.request.setRequestURI("/mvc-showcase/bar");
109110
UriComponents result = ServletUriComponentsBuilder.fromRequest(this.request).build();
110-
assertEquals("http://localhost/foo/bar", result.toUriString());
111+
assertEquals("http://localhost/prefix/bar", result.toUriString());
111112
}
112113

113-
@Test
114+
@Test // SPR-16650
114115
public void fromRequestWithForwardedPrefixTrailingSlash() {
115-
this.request.setRequestURI("/bar");
116116
this.request.addHeader("X-Forwarded-Prefix", "/foo/");
117+
this.request.setContextPath("/spring-mvc-showcase");
118+
this.request.setRequestURI("/spring-mvc-showcase/bar");
117119
UriComponents result = ServletUriComponentsBuilder.fromRequest(this.request).build();
118120
assertEquals("http://localhost/foo/bar", result.toUriString());
119121
}
120122

123+
@Test // SPR-16650
124+
public void fromRequestWithForwardedPrefixRoot() {
125+
this.request.addHeader("X-Forwarded-Prefix", "/");
126+
this.request.setContextPath("/mvc-showcase");
127+
this.request.setRequestURI("/mvc-showcase/bar");
128+
UriComponents result = ServletUriComponentsBuilder.fromRequest(this.request).build();
129+
assertEquals("http://localhost/bar", result.toUriString());
130+
}
131+
121132
@Test
122133
public void fromContextPath() {
123134
this.request.setRequestURI("/mvc-showcase/data/param");
@@ -126,13 +137,13 @@ public void fromContextPath() {
126137
assertEquals("http://localhost/mvc-showcase", result);
127138
}
128139

129-
@Test
140+
@Test // SPR-16650
130141
public void fromContextPathWithForwardedPrefix() {
131142
this.request.addHeader("X-Forwarded-Prefix", "/prefix");
132143
this.request.setContextPath("/mvc-showcase");
133144
this.request.setRequestURI("/mvc-showcase/simple");
134145
String result = ServletUriComponentsBuilder.fromContextPath(this.request).build().toUriString();
135-
assertEquals("http://localhost/prefix/mvc-showcase", result);
146+
assertEquals("http://localhost/prefix", result);
136147
}
137148

138149
@Test
@@ -144,14 +155,14 @@ public void fromServletMapping() {
144155
assertEquals("http://localhost/mvc-showcase/app", result);
145156
}
146157

147-
@Test
158+
@Test // SPR-16650
148159
public void fromServletMappingWithForwardedPrefix() {
149160
this.request.addHeader("X-Forwarded-Prefix", "/prefix");
150161
this.request.setContextPath("/mvc-showcase");
151162
this.request.setServletPath("/app");
152163
this.request.setRequestURI("/mvc-showcase/app/simple");
153164
String result = ServletUriComponentsBuilder.fromServletMapping(this.request).build().toUriString();
154-
assertEquals("http://localhost/prefix/mvc-showcase/app", result);
165+
assertEquals("http://localhost/prefix/app", result);
155166
}
156167

157168
@Test
@@ -168,9 +179,7 @@ public void fromCurrentRequest() {
168179
}
169180
}
170181

171-
// SPR-10272
172-
173-
@Test
182+
@Test // SPR-10272
174183
public void pathExtension() {
175184
this.request.setRequestURI("/rest/books/6.json");
176185
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromRequestUri(this.request);

0 commit comments

Comments
 (0)