From 477ee0d42a3d34f3b9e7b99370a5bfef1258cfe6 Mon Sep 17 00:00:00 2001 From: Matt Benson Date: Tue, 19 Apr 2016 10:53:00 -0500 Subject: [PATCH] Allow ResourceController to handle resource paths of arbitrary depth File paths with slashes seem reasonable, so this change adds support for those. Unfortunately, but unavoidably this requires some manipulation of the servlet request, until changes can be made in Spring MVC. See https://jira.spring.io/browse/SPR-14193. Fixes gh-376 --- .../server/resource/ResourceController.java | 46 ++++++++++++++++--- .../resource/ResourceControllerTests.java | 16 ++++++- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/resource/ResourceController.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/resource/ResourceController.java index fdd5c948..409adc50 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/resource/ResourceController.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/resource/ResourceController.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2016 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. @@ -20,11 +20,14 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import javax.servlet.http.HttpServletRequest; + import org.springframework.cloud.config.server.environment.EnvironmentRepository; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.util.AntPathMatcher; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; @@ -32,6 +35,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.HandlerMapping; import static org.springframework.cloud.config.server.support.EnvironmentPropertySource.prepareEnvironment; import static org.springframework.cloud.config.server.support.EnvironmentPropertySource.resolvePlaceholders; @@ -61,10 +65,25 @@ public class ResourceController { this.environmentRepository = environmentRepository; } - @RequestMapping("/{name}/{profile}/{label}/{path:.*}") - public synchronized String resolve(@PathVariable String name, + @RequestMapping("/{name}/{profile}/{label}/**") + public String resolve(@PathVariable String name, @PathVariable String profile, @PathVariable String label, - @PathVariable String path) throws IOException { + HttpServletRequest request) throws IOException { + String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + if (pattern == null) { + throw new IllegalStateException("Required request attribute '" + + HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE + "' is not set"); + } + String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); + if (path == null) { + throw new IllegalStateException("Required request attribute '" + + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set"); + } + path = new AntPathMatcher().extractPathWithinPattern(pattern, path); + return resolve(name, profile, label, path); + } + + synchronized String resolve(String name, String profile, String label, String path) throws IOException { if (label != null && label.contains("(_)")) { // "(_)" is uncommon in a git branch name, but "/" cannot be matched // by Spring MVC @@ -81,10 +100,25 @@ public class ResourceController { } } - @RequestMapping(value = "/{name}/{profile}/{label}/{path:.*}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @RequestMapping(value = "/{name}/{profile}/{label}/**", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public synchronized byte[] binary(@PathVariable String name, @PathVariable String profile, @PathVariable String label, - @PathVariable String path) throws IOException { + HttpServletRequest request) throws IOException { + String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + if (pattern == null) { + throw new IllegalStateException("Required request attribute '" + + HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE + "' is not set"); + } + String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); + if (path == null) { + throw new IllegalStateException("Required request attribute '" + + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set"); + } + path = new AntPathMatcher().extractPathWithinPattern(pattern, path); + return binary(name, profile, label, path); + } + + synchronized byte[] binary(String name, String profile, String label, String path) throws IOException { if (label != null && label.contains("(_)")) { // "(_)" is uncommon in a git branch name, but "/" cannot be matched // by Spring MVC diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/resource/ResourceControllerTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/resource/ResourceControllerTests.java index a5ae28fa..34c52ad9 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/resource/ResourceControllerTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/resource/ResourceControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2016 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. @@ -86,6 +86,13 @@ public class ResourceControllerTests { assertEquals("foo: dev_bar/spam", resource); } + @Test + public void resourceWithSlash() throws Exception { + this.environmentRepository.setSearchLocations("classpath:/test"); + String resource = this.controller.resolve("foo", "bar", "dev", "spam/foo.txt"); + assertEquals("foo: dev_bar/spam", resource); + } + @Test public void labelWithSlashForBinary() throws Exception { this.environmentRepository.setSearchLocations("classpath:/test"); @@ -93,4 +100,11 @@ public class ResourceControllerTests { assertEquals("foo: dev_bar/spam", new String(resource)); } + @Test + public void resourceWithSlashForBinary() throws Exception { + this.environmentRepository.setSearchLocations("classpath:/test"); + byte[] resource = this.controller.binary("foo", "bar", "dev", "spam/foo.txt"); + assertEquals("foo: dev_bar/spam", new String(resource)); + } + }