DATAREST-815 - Tweaked HalBrowser controller implementation to consider proxied requests.

We now use the APIs in place in Spring MVC to make sure we create proper redirects for requests that carry proxy headers so that the redirect target is still going through the proxy.
This commit is contained in:
Oliver Gierke
2016-04-27 15:33:40 +02:00
parent 8d249b4b9c
commit 954c49be89
2 changed files with 50 additions and 35 deletions

View File

@@ -17,15 +17,14 @@ package org.springframework.data.rest.webmvc.halbrowser;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.UriComponents;
/**
* Controller with a few convenience redirects to expose the HAL browser shipped as static content.
@@ -37,22 +36,7 @@ import org.springframework.web.servlet.view.RedirectView;
public class HalBrowser {
private static String BROWSER = "/browser";
public static String BROWSER_INDEX = BROWSER.concat("/index.html");
private final RepositoryRestConfiguration configuration;
/**
* Creates a new {@link HalBrowser} for the given {@link RepositoryRestConfiguration}.
*
* @param configuration must not be {@literal null}.
*/
@Autowired
public HalBrowser(RepositoryRestConfiguration configuration) {
Assert.notNull(configuration, "RepositoryRestConfiguration must not be null!");
this.configuration = configuration;
}
private static String INDEX = "/index.html";
/**
* Redirects requests to the API root asking for HTML to the HAL browser.
@@ -61,7 +45,7 @@ public class HalBrowser {
*/
@RequestMapping(value = { "/", "" }, method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
public View index(HttpServletRequest request) {
return browser(request);
return getRedirectView(request, false);
}
/**
@@ -71,10 +55,30 @@ public class HalBrowser {
*/
@RequestMapping(value = "/browser", method = RequestMethod.GET)
public View browser(HttpServletRequest request) {
return getRedirectView(request, request.getRequestURI().endsWith("/browser"));
}
String contextPath = request.getContextPath();
String basePath = configuration.getBasePath().toString();
/**
* Returns the View to redirect to to access the HAL browser.
*
* @param request must not be {@literal null}.
* @param browserRelative
* @return
*/
private View getRedirectView(HttpServletRequest request, boolean browserRelative) {
return new RedirectView(basePath.concat(BROWSER_INDEX).concat("#").concat(contextPath.concat(basePath)), true);
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromRequest(request);
UriComponents components = builder.build();
String path = components.getPath() == null ? "" : components.getPath();
if (!browserRelative) {
builder.path(BROWSER);
}
builder.path(INDEX);
builder.fragment(browserRelative ? path.substring(0, path.lastIndexOf("/browser")) : path);
return new RedirectView(builder.build().toUriString());
}
}

View File

@@ -15,18 +15,12 @@
*/
package org.springframework.data.rest.webmvc.halbrowser;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.Collections;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.data.rest.core.config.EnumTranslationConfiguration;
import org.springframework.data.rest.core.config.MetadataConfiguration;
import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -51,14 +45,12 @@ public class HalBrowserUnitTests {
@Test
public void createsContextRelativeRedirectForBrowser() throws Exception {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration(new ProjectionDefinitionConfiguration(),
new MetadataConfiguration(), mock(EnumTranslationConfiguration.class));
MockHttpServletResponse response = new MockHttpServletResponse();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/context");
request.setContextPath("/context");
View view = new HalBrowser(configuration).browser(request);
View view = new HalBrowser().browser(request);
assertThat(view, is(instanceOf(RedirectView.class)));
@@ -66,7 +58,26 @@ public class HalBrowserUnitTests {
UriComponents components = UriComponentsBuilder.fromUriString(response.getHeader(HttpHeaders.LOCATION)).build();
assertThat(components.getPath(), Matchers.startsWith("/context"));
assertThat(components.getPath(), startsWith("/context"));
assertThat(components.getFragment(), is("/context"));
}
@Test
public void producesProxyRelativeRedirectIfNecessary() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/browser");
request.addHeader("X-Forwarded-Host", "somehost");
request.addHeader("X-Forwarded-Port", "4711");
request.addHeader("X-Forwarded-Proto", "https");
request.addHeader("X-Forwarded-Prefix", "/prefix");
View view = new HalBrowser().browser(request);
assertThat(view, is(instanceOf(RedirectView.class)));
String url = ((RedirectView) view).getUrl();
assertThat(url, startsWith("https://somehost:4711/prefix"));
assertThat(url, endsWith("/prefix"));
}
}