#758 - Centralize all Forward header handling.

Spring 5.1 is centralizing all Forward header handling. This moves critical bits into one location, making it easy to completely remove once we rebaseline against this version.

Also adds a test profile to ensure Spring 5.1 doesn't break anything.

Original pull request: #717.
This commit is contained in:
Greg Turnquist
2018-05-22 14:04:09 -05:00
committed by Oliver Drotbohm
parent 267ff45043
commit bb4a4a615c
6 changed files with 134 additions and 23 deletions

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.hateoas.mvc;
import static org.springframework.util.StringUtils.*;
import static org.springframework.hateoas.mvc.ForwardedHeader.handleXForwardedSslHeader;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;
@@ -28,6 +28,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.hateoas.Affordance;
import org.springframework.hateoas.Link;
@@ -306,27 +307,37 @@ public class ControllerLinkBuilder extends LinkBuilderSupport<ControllerLinkBuil
*
* @return
*/
public static UriComponentsBuilder getBuilder() {
public static UriComponentsBuilder getBuilder() {
if (RequestContextHolder.getRequestAttributes() == null) {
return UriComponentsBuilder.fromPath("/");
}
HttpServletRequest request = getCurrentRequest();
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromServletMapping(request);
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromServletMapping(request);
// special case handling for X-Forwarded-Ssl:
// apply it, but only if X-Forwarded-Proto is unset.
String forwardedSsl = request.getHeader("X-Forwarded-Ssl");
ForwardedHeader forwarded = ForwardedHeader.of(request.getHeader(ForwardedHeader.NAME));
String proto = hasText(forwarded.getProto()) ? forwarded.getProto() : request.getHeader("X-Forwarded-Proto");
if (!hasText(proto) && hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on")) {
builder.scheme("https");
// Spring 5.1 can handle X-Forwarded-Ssl headers...
if (isSpringAtLeast5_1()) {
return builder;
} else {
return handleXForwardedSslHeader(request, builder);
}
}
return builder;
/**
* Check if the current version of Spring Framework is 5.1 or higher.
*
* @return
*/
private static boolean isSpringAtLeast5_1() {
String versionOfSpringFramework = ApplicationContext.class.getPackage().getImplementationVersion();
String[] parts = versionOfSpringFramework.split("\\.");
int majorVersion = Integer.parseInt(parts[0]);
int minorVersion = Integer.parseInt(parts[1]);
return (majorVersion >= 5 && minorVersion >= 1) || (majorVersion > 5);
}
/**
@@ -361,13 +372,13 @@ public class ControllerLinkBuilder extends LinkBuilderSupport<ControllerLinkBuil
private static class CachingAnnotationMappingDiscoverer implements MappingDiscoverer {
private final @Delegate AnnotationMappingDiscoverer delegate;
private final Map<String, UriTemplate> templates = new ConcurrentReferenceHashMap<>();
private final Map<String, UriTemplate> templates = new ConcurrentReferenceHashMap<>();
public UriTemplate getMappingAsUriTemplate(Class<?> type, Method method) {
String mapping = delegate.getMapping(type, method);
return templates.computeIfAbsent(mapping, UriTemplate::new);
return templates.computeIfAbsent(mapping, UriTemplate::new);
}
}

View File

@@ -15,38 +15,70 @@
*/
package org.springframework.hateoas.mvc;
import static org.springframework.util.StringUtils.hasText;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Value object to partially implement the {@literal Forwarded} header defined in RFC 7239.
*
* @author Oliver Gierke
* @see http://tools.ietf.org/html/rfc7239
* @deprecated In Spring 5.1, all Forwarded headers will by handled by Spring MVC.
*/
@Deprecated
class ForwardedHeader {
public static String NAME = "Forwarded";
private static final ForwardedHeader NO_HEADER = new ForwardedHeader(Collections.emptyMap());
private final Map<String, String> elements;
private ForwardedHeader(Map<String, String> elements) {
this.elements = elements;
}
/**
* Utility method to pull handling of {@literal X-Forwarded-Ssl} into a class that will be removed when
* rebaselined against Spring 5.1
*
* @param request
* @param builder
* @return
* @deprecated No longer needed with Spring 5.1
*/
@Deprecated
public static UriComponentsBuilder handleXForwardedSslHeader(HttpServletRequest request, UriComponentsBuilder builder) {
// special case handling for X-Forwarded-Ssl:
// apply it, but only if X-Forwarded-Proto is unset.
String forwardedSsl = request.getHeader("X-Forwarded-Ssl");
ForwardedHeader forwarded = ForwardedHeader.of(request.getHeader("Forwarded"));
String proto = hasText(forwarded.getProto()) ? forwarded.getProto() : request.getHeader("X-Forwarded-Proto");
if (!hasText(proto) && hasText(forwardedSsl) && forwardedSsl.equalsIgnoreCase("on")) {
builder.scheme("https");
}
return builder;
}
/**
* Creates a new {@link ForwardedHeader} from the given source.
*
* @param source can be {@literal null}.
* @return
*/
public static ForwardedHeader of(String source) {
static ForwardedHeader of(String source) {
if (!StringUtils.hasText(source)) {
return NO_HEADER;
@@ -67,7 +99,7 @@ class ForwardedHeader {
*
* @return
*/
public String getProto() {
String getProto() {
return elements.get("proto");
}
@@ -76,7 +108,7 @@ class ForwardedHeader {
*
* @return
*/
public String getHost() {
String getHost() {
return elements.get("host");
}
}