diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java index c19dcb24fd..51aaed9dc4 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.devtools.remote.server.AccessManager; import org.springframework.boot.devtools.remote.server.Dispatcher; @@ -76,6 +77,9 @@ public class RemoteDevToolsAutoConfiguration { @Autowired private DevToolsProperties properties; + @Autowired + private ServerProperties serverProperties; + @Bean @ConditionalOnMissingBean public AccessManager remoteDevToolsAccessManager() { @@ -87,8 +91,9 @@ public class RemoteDevToolsAutoConfiguration { @Bean public HandlerMapper remoteDevToolsHealthCheckHandlerMapper() { Handler handler = new HttpStatusHandler(); - return new UrlHandlerMapper(this.properties.getRemote().getContextPath(), - handler); + return new UrlHandlerMapper((this.serverProperties.getContextPath() == null ? "" + : this.serverProperties.getContextPath()) + + this.properties.getRemote().getContextPath(), handler); } @Bean @@ -108,6 +113,9 @@ public class RemoteDevToolsAutoConfiguration { @Autowired private DevToolsProperties properties; + @Autowired + private ServerProperties serverProperties; + @Bean @ConditionalOnMissingBean public SourceFolderUrlFilter remoteRestartSourceFolderUrlFilter() { @@ -124,7 +132,9 @@ public class RemoteDevToolsAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "remoteRestartHanderMapper") public UrlHandlerMapper remoteRestartHanderMapper(HttpRestartServer server) { - String url = this.properties.getRemote().getContextPath() + "/restart"; + String url = (this.serverProperties.getContextPath() == null ? "" + : this.serverProperties.getContextPath()) + + this.properties.getRemote().getContextPath() + "/restart"; logger.warn("Listening for remote restart updates on " + url); Handler handler = new HttpRestartServerHandler(server); return new UrlHandlerMapper(url, handler); @@ -141,11 +151,16 @@ public class RemoteDevToolsAutoConfiguration { @Autowired private DevToolsProperties properties; + @Autowired + private ServerProperties serverProperties; + @Bean @ConditionalOnMissingBean(name = "remoteDebugHanderMapper") public UrlHandlerMapper remoteDebugHanderMapper( @Qualifier("remoteDebugHttpTunnelServer") HttpTunnelServer server) { - String url = this.properties.getRemote().getContextPath() + "/debug"; + String url = (this.serverProperties.getContextPath() == null ? "" + : this.serverProperties.getContextPath()) + + this.properties.getRemote().getContextPath() + "/debug"; logger.warn("Listening for remote debug traffic on " + url); Handler handler = new HttpTunnelServerHandler(server); return new UrlHandlerMapper(url, handler); diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfigurationTests.java index 682f598016..acb2ad1c4e 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfigurationTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfigurationTests.java @@ -137,6 +137,17 @@ public class RemoteDevToolsAutoConfigurationTests { assertRestartInvoked(true); } + @Test + public void invokeRestartWithCustomServerContextPath() throws Exception { + loadContext("spring.devtools.remote.secret:supersecret", + "server.context-path:/test"); + DispatcherFilter filter = this.context.getBean(DispatcherFilter.class); + this.request.setRequestURI("/test" + DEFAULT_CONTEXT_PATH + "/restart"); + this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret"); + filter.doFilter(this.request, this.response, this.chain); + assertRestartInvoked(true); + } + @Test public void disableRestart() throws Exception { loadContext("spring.devtools.remote.secret:supersecret", @@ -155,6 +166,17 @@ public class RemoteDevToolsAutoConfigurationTests { assertTunnelInvoked(true); } + @Test + public void invokeTunnelWithCustomServerContextPath() throws Exception { + loadContext("spring.devtools.remote.secret:supersecret", + "server.context-path:/test"); + DispatcherFilter filter = this.context.getBean(DispatcherFilter.class); + this.request.setRequestURI("/test" + DEFAULT_CONTEXT_PATH + "/debug"); + this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret"); + filter.doFilter(this.request, this.response, this.chain); + assertTunnelInvoked(true); + } + @Test public void invokeTunnelWithCustomHeaderName() throws Exception { loadContext("spring.devtools.remote.secret:supersecret", @@ -185,6 +207,18 @@ public class RemoteDevToolsAutoConfigurationTests { assertThat(this.response.getStatus(), equalTo(200)); } + @Test + public void devToolsHealthWithCustomServerContextPathReturns200() throws Exception { + loadContext("spring.devtools.remote.secret:supersecret", + "server.context-path:/test"); + DispatcherFilter filter = this.context.getBean(DispatcherFilter.class); + this.request.setRequestURI("/test" + DEFAULT_CONTEXT_PATH); + this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret"); + this.response.setStatus(500); + filter.doFilter(this.request, this.response, this.chain); + assertThat(this.response.getStatus(), equalTo(200)); + } + private void assertTunnelInvoked(boolean value) { assertThat(this.context.getBean(MockHttpTunnelServer.class).invoked, equalTo(value));