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
This commit is contained in:
Matt Benson
2016-04-19 10:53:00 -05:00
committed by Dave Syer
parent 1e38abfc6b
commit 477ee0d42a
2 changed files with 55 additions and 7 deletions

View File

@@ -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

View File

@@ -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));
}
}