diff --git a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java index 51688956..0a0d625f 100755 --- a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java +++ b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilder.java @@ -15,6 +15,8 @@ */ package org.springframework.hateoas.mvc; +import static org.springframework.util.StringUtils.*; + import java.lang.reflect.Method; import java.net.URI; @@ -26,7 +28,6 @@ import org.springframework.hateoas.core.DummyInvocationUtils; import org.springframework.hateoas.core.LinkBuilderSupport; import org.springframework.hateoas.core.MappingDiscoverer; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @@ -186,24 +187,29 @@ public class ControllerLinkBuilder extends LinkBuilderSupport emptyMap()); + + private final Map elements; + + private ForwardedHeader(Map elements) { + this.elements = elements; + } + + /** + * Creates a new {@link ForwardedHeader} from the given source. + * + * @param source can be {@literal null}. + * @return + */ + public static ForwardedHeader of(String source) { + + if (!StringUtils.hasText(source)) { + return NO_HEADER; + } + + Map elements = new HashMap(); + + for (String part : source.split(";")) { + + String[] keyValue = part.split("="); + + if (keyValue.length != 2) { + continue; + } + + elements.put(keyValue[0].trim(), keyValue[1].trim()); + } + + Assert.notNull(elements, "Forwarded elements must not be null!"); + Assert.isTrue(!elements.isEmpty(), "At least one forwarded element needs to be present!"); + + return new ForwardedHeader(elements); + } + + /** + * Returns the value defined for the {@code proto} parameter of the header. + * + * @return + */ + public String getProto() { + return elements.get("proto"); + } + + /** + * Returns the value defined for the {@code host} parameter of the header. + * + * @return + */ + public String getHost() { + return elements.get("host"); + } +} diff --git a/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java b/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java index 0e180a6e..e06d2f56 100644 --- a/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderUnitTest.java @@ -378,6 +378,51 @@ public class ControllerLinkBuilderUnitTest extends TestUtils { assertThat(link.getHref(), endsWith("/root")); } + /** + * @see #257, #107 + */ + @Test + public void usesXForwardedProtoHeaderAsLinkSchema() { + + for (String proto : Arrays.asList("http", "https")) { + + setUp(); + request.addHeader("X-Forwarded-Proto", proto); + + Link link = linkTo(PersonControllerImpl.class).withSelfRel(); + assertThat(link.getHref(), startsWith(proto + "://")); + } + } + + /** + * @see #257, #107 + */ + @Test + public void usesProtoValueFromForwardedHeaderAsLinkSchema() { + + for (String proto : Arrays.asList("http", "https")) { + + setUp(); + request.addHeader("Forwarded", new String[] { "proto=" + proto }); + + Link link = linkTo(PersonControllerImpl.class).withSelfRel(); + assertThat(link.getHref(), startsWith(proto.concat("://"))); + } + } + + /** + * @see #257, #107 + */ + @Test + public void favorsStandardForwardHeaderOverXForwardedProto() { + + request.addHeader("X-Forwarded-Proto", "foo"); + request.addHeader(ForwardedHeader.NAME, "proto=bar"); + + Link link = linkTo(PersonControllerImpl.class).withSelfRel(); + assertThat(link.getHref(), startsWith("bar://")); + } + private static UriComponents toComponents(Link link) { return UriComponentsBuilder.fromUriString(link.getHref()).build(); } diff --git a/src/test/java/org/springframework/hateoas/mvc/ForwardedHeaderUnitTests.java b/src/test/java/org/springframework/hateoas/mvc/ForwardedHeaderUnitTests.java new file mode 100644 index 00000000..32d38efa --- /dev/null +++ b/src/test/java/org/springframework/hateoas/mvc/ForwardedHeaderUnitTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.hateoas.mvc; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Unit tests for {@link ForwardedHeader}. + * + * @author Oliver Gierke + */ +public class ForwardedHeaderUnitTests { + + /** + * @see #257 + */ + @Test + public void detectsProtoValue() { + assertThat(ForwardedHeader.of("for=192.0.2.60;proto=http").getProto(), is("http")); + } + + /** + * @see #257 + */ + @Test + public void detectsHostValue() { + assertThat(ForwardedHeader.of("host=localhost;proto=http").getHost(), is("localhost")); + } + + /** + * @see #257 + */ + @Test + public void returnsNullObjectForNullSource() { + + ForwardedHeader header = ForwardedHeader.of(null); + + assertThat(header, is(notNullValue())); + assertThat(header.getHost(), is(nullValue())); + assertThat(header.getProto(), is(nullValue())); + } +}