Skip to content

Commit 47cefe6

Browse files
Oliver Trosienodrotbohm
Oliver Trosien
authored andcommitted
#509 - Align ControllerLinkBuilder's base URI generation with Spring Framework.
Removed the custom handling of proxy headers in ControllerLinkBuilder in favor of the behavior of UriComponentsBuilder in Spring Framework. The only thing that we keep custom is the handling of X-Forwarded-Ssl as it's rather non-standard. Original pull request: #519.
1 parent 8f53d3f commit 47cefe6

File tree

2 files changed

+47
-48
lines changed

2 files changed

+47
-48
lines changed

src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java

Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
*/
1616
package org.springframework.hateoas.mvc;
1717

18-
import static org.springframework.util.StringUtils.*;
19-
2018
import lombok.RequiredArgsConstructor;
2119
import lombok.experimental.Delegate;
2220

21+
import static org.springframework.util.StringUtils.hasText;
22+
2323
import java.lang.reflect.Method;
2424
import java.net.URI;
2525
import java.util.Map;
@@ -32,6 +32,7 @@
3232
import org.springframework.hateoas.core.DummyInvocationUtils;
3333
import org.springframework.hateoas.core.LinkBuilderSupport;
3434
import org.springframework.hateoas.core.MappingDiscoverer;
35+
import org.springframework.http.server.ServletServerHttpRequest;
3536
import org.springframework.util.Assert;
3637
import org.springframework.util.ConcurrentReferenceHashMap;
3738
import org.springframework.web.bind.annotation.RequestMapping;
@@ -52,6 +53,7 @@
5253
* @author Greg Turnquist
5354
* @author Kevin Conaway
5455
* @author Andrew Naydyonock
56+
* @author Oliver Trosien
5557
*/
5658
public class ControllerLinkBuilder extends LinkBuilderSupport<ControllerLinkBuilder> {
5759

@@ -253,56 +255,29 @@ public String toString() {
253255
}
254256

255257
/**
256-
* Returns a {@link UriComponentsBuilder} obtained from the current servlet mapping with the host tweaked in case the
257-
* request contains an {@code X-Forwarded-Host} header and the scheme tweaked in case the request contains an
258-
* {@code X-Forwarded-Ssl} header
258+
* Returns a {@link UriComponentsBuilder} obtained from the current servlet mapping with
259+
* scheme tweaked in case the request contains an {@code X-Forwarded-Ssl} header, which is not (yet)
260+
* supported by the underlying {@link UriComponentsBuilder}.
259261
*
260262
* @return
261263
*/
262264
static UriComponentsBuilder getBuilder() {
263-
264-
HttpServletRequest request = getCurrentRequest();
265-
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromServletMapping(request);
266-
267-
ForwardedHeader forwarded = ForwardedHeader.of(request.getHeader(ForwardedHeader.NAME));
268-
String proto = hasText(forwarded.getProto()) ? forwarded.getProto() : request.getHeader("X-Forwarded-Proto");
269-
String forwardedSsl = request.getHeader("X-Forwarded-Ssl");
270-
271-
if (hasText(proto)) {
272-
builder.scheme(proto);
273-
} else if (hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on")) {
274-
builder.scheme("https");
275-
}
276-
277-
String host = forwarded.getHost();
278-
host = hasText(host) ? host : request.getHeader("X-Forwarded-Host");
279-
280-
if (!hasText(host)) {
281-
return builder;
282-
}
283-
284-
String[] hosts = commaDelimitedListToStringArray(host);
285-
String hostToUse = hosts[0];
286-
287-
if (hostToUse.contains(":")) {
288-
289-
String[] hostAndPort = split(hostToUse, ":");
290-
291-
builder.host(hostAndPort[0]);
292-
builder.port(Integer.parseInt(hostAndPort[1]));
293-
294-
} else {
295-
builder.host(hostToUse);
296-
builder.port(-1); // reset port if it was forwarded from default port
297-
}
298-
299-
String port = request.getHeader("X-Forwarded-Port");
300-
301-
if (hasText(port)) {
302-
builder.port(Integer.parseInt(port));
303-
}
304-
305-
return builder;
265+
266+
HttpServletRequest request = getCurrentRequest();
267+
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request));
268+
269+
// special case handling for X-Forwarded-Ssl:
270+
// apply it, but only if X-Forwarded-Proto is unset.
271+
272+
String forwardedSsl = request.getHeader("X-Forwarded-Ssl");
273+
ForwardedHeader forwarded = ForwardedHeader.of(request.getHeader(ForwardedHeader.NAME));
274+
String proto = hasText(forwarded.getProto()) ? forwarded.getProto() : request.getHeader("X-Forwarded-Proto");
275+
276+
if (!hasText(proto) && hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on")) {
277+
builder.scheme("https");
278+
}
279+
280+
return builder;
306281
}
307282

308283
/**

src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,30 @@ public void addsOptionalRequestParameterTemplateForMissingValue() {
532532
assertThat(components.getQueryParams().get("query"), is(nullValue()));
533533
}
534534

535+
/**
536+
* @see #509
537+
*/
538+
@Test
539+
public void supportsTwoProxiesAddingXForwardedPort() {
540+
request.addHeader("X-Forwarded-Port", "1443,8443");
541+
request.addHeader("X-Forwarded-Host", "proxy1,proxy2");
542+
543+
Link link = linkTo(PersonControllerImpl.class).withSelfRel();
544+
assertThat(link.getHref(), startsWith("http://proxy1:1443"));
545+
}
546+
547+
/**
548+
* @see #509
549+
*/
550+
@Test
551+
public void resolvesAmbiguousXForwardedHeaders() {
552+
request.addHeader("X-Forwarded-Proto", "http");
553+
request.addHeader("X-Forwarded-Ssl", "on");
554+
555+
Link link = linkTo(PersonControllerImpl.class).withSelfRel();
556+
assertThat(link.getHref(), startsWith("http://"));
557+
}
558+
535559
private static UriComponents toComponents(Link link) {
536560
return UriComponentsBuilder.fromUriString(link.expand().getHref()).build();
537561
}

0 commit comments

Comments
 (0)