Refactor ResourceWebHandlerTests
This commit is contained in:
parent
5c1cdcb245
commit
ff14c5121d
|
|
@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
|
@ -78,29 +79,25 @@ class ResourceWebHandlerTests {
|
|||
|
||||
private static final Duration TIMEOUT = Duration.ofSeconds(1);
|
||||
|
||||
private final ClassPathResource testResource = new ClassPathResource("test/", getClass());
|
||||
private final ClassPathResource testAlternatePathResource = new ClassPathResource("testalternatepath/", getClass());
|
||||
private final ClassPathResource webjarsResource = new ClassPathResource("META-INF/resources/webjars/");
|
||||
private static final ClassPathResource testResource = new ClassPathResource("test/", ResourceWebHandlerTests.class);
|
||||
private static final ClassPathResource testAlternatePathResource = new ClassPathResource("testalternatepath/", ResourceWebHandlerTests.class);
|
||||
private static final ClassPathResource webjarsResource = new ClassPathResource("META-INF/resources/webjars/");
|
||||
|
||||
|
||||
@Nested
|
||||
class ResourceHandlingTests {
|
||||
|
||||
private ResourceWebHandler handler;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
List<Resource> locations = List.of(
|
||||
this.testResource,
|
||||
this.testAlternatePathResource,
|
||||
this.webjarsResource);
|
||||
|
||||
this.handler = new ResourceWebHandler();
|
||||
this.handler.setLocations(locations);
|
||||
this.handler.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS));
|
||||
this.handler.setLocations(List.of(testResource, testAlternatePathResource, webjarsResource));
|
||||
this.handler.afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getResource() throws Exception {
|
||||
void servesResource() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
|
|
@ -109,37 +106,25 @@ class ResourceWebHandlerTests {
|
|||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertThat(headers.getCacheControl()).isEqualTo("max-age=3600");
|
||||
assertThat(headers.containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(headers.getLastModified() / 1000);
|
||||
assertThat(headers.getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(headers.get("Accept-Ranges")).hasSize(1);
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getResourceHttpHeader() throws Exception {
|
||||
void supportsHeadRequests() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.head(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat((Object) exchange.getResponse().getStatusCode()).isNull();
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertThat(headers.getCacheControl()).isEqualTo("max-age=3600");
|
||||
assertThat(headers.containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(headers.getLastModified() / 1000);
|
||||
assertThat(headers.getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(headers.get("Accept-Ranges")).hasSize(1);
|
||||
|
||||
StepVerifier.create(exchange.getResponse().getBody())
|
||||
.verifyComplete();
|
||||
assertResponseBodyIsEmpty(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceHttpOptions() {
|
||||
void supportsOptionsRequests() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.options(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
|
|
@ -150,40 +135,7 @@ class ResourceWebHandlerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void getResourceNoCache() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.setCacheControl(CacheControl.noStore());
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
MockServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().getCacheControl()).isEqualTo("no-store");
|
||||
assertThat(response.getHeaders().containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(response.getHeaders().getLastModified() / 1000);
|
||||
assertThat(response.getHeaders().getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(response.getHeaders().get("Accept-Ranges")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getVersionedResource() throws Exception {
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.addFixedVersionStrategy("versionString", "/**");
|
||||
this.handler.setResourceResolvers(List.of(versionResolver, new PathResourceResolver()));
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "versionString/foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat(exchange.getResponse().getHeaders().getETag()).isEqualTo("W/\"versionString\"");
|
||||
assertThat(exchange.getResponse().getHeaders().getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(exchange.getResponse().getHeaders().get("Accept-Ranges")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceWithHtmlMediaType() throws Exception {
|
||||
void servesHtmlResources() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.html");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
|
|
@ -191,52 +143,6 @@ class ResourceWebHandlerTests {
|
|||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.TEXT_HTML);
|
||||
assertThat(headers.getCacheControl()).isEqualTo("max-age=3600");
|
||||
assertThat(headers.containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.html") / 1000).isEqualTo(headers.getLastModified() / 1000);
|
||||
assertThat(headers.getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(headers.get("Accept-Ranges")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromAlternatePath() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "baz.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertThat(headers.getCacheControl()).isEqualTo("max-age=3600");
|
||||
assertThat(headers.containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("testalternatepath/baz.css") / 1000).isEqualTo(headers.getLastModified() / 1000);
|
||||
assertThat(headers.getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(headers.get("Accept-Ranges")).hasSize(1);
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromSubDirectory() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "js/foo.js");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat(exchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.parseMediaType("application/javascript"));
|
||||
assertResponseBody(exchange, "function foo() { console.log(\"hello world\"); }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromSubDirectoryOfAlternatePath() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "js/baz.js");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("application/javascript"));
|
||||
assertResponseBody(exchange, "function foo() { console.log(\"hello world\"); }");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -271,44 +177,7 @@ class ResourceWebHandlerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromFileSystem() throws Exception {
|
||||
String packagePath = ClassUtils.classPackageAsResourcePath(getClass());
|
||||
String path = Paths.get("src/test/resources", packagePath).normalize() + "/";
|
||||
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setLocations(List.of(new FileSystemResource(path)));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, UriUtils.encodePath("test/foo with spaces.css", UTF_8));
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test // gh-27538, gh-27624
|
||||
void filterNonExistingLocations() throws Exception {
|
||||
List<Resource> inputLocations = List.of(
|
||||
new ClassPathResource("test/", getClass()),
|
||||
new ClassPathResource("testalternatepath/", getClass()),
|
||||
new ClassPathResource("nosuchpath/", getClass()));
|
||||
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setLocations(inputLocations);
|
||||
handler.setOptimizeLocations(true);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
List<Resource> actual = handler.getLocations();
|
||||
assertThat(actual).hasSize(2);
|
||||
assertThat(actual.get(0).getURL().toString()).endsWith("test/");
|
||||
assertThat(actual.get(1).getURL().toString()).endsWith("testalternatepath/");
|
||||
}
|
||||
|
||||
@Test // SPR-14577
|
||||
// SPR-14577
|
||||
void getMediaTypeWithFavorPathExtensionOff() throws Exception {
|
||||
List<Resource> paths = List.of(new ClassPathResource("test/", getClass()));
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
|
|
@ -324,215 +193,6 @@ class ResourceWebHandlerTests {
|
|||
assertThat(exchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_HTML);
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidPath() throws Exception {
|
||||
|
||||
// Use mock ResourceResolver: i.e. we're only testing upfront validations...
|
||||
|
||||
Resource resource = mock();
|
||||
given(resource.getFilename()).willThrow(new AssertionError("Resource should not be resolved"));
|
||||
given(resource.getInputStream()).willThrow(new AssertionError("Resource should not be resolved"));
|
||||
ResourceResolver resolver = mock();
|
||||
given(resolver.resolveResource(any(), any(), any(), any())).willReturn(Mono.just(resource));
|
||||
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setLocations(List.of(new ClassPathResource("test/", getClass())));
|
||||
handler.setResourceResolvers(List.of(resolver));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
testInvalidPath("../testsecret/secret.txt", handler);
|
||||
testInvalidPath("test/../../testsecret/secret.txt", handler);
|
||||
testInvalidPath(":/../../testsecret/secret.txt", handler);
|
||||
|
||||
Resource location = new UrlResource(getClass().getResource("./test/"));
|
||||
handler.setLocations(List.of(location));
|
||||
Resource secretResource = new UrlResource(getClass().getResource("testsecret/secret.txt"));
|
||||
String secretPath = secretResource.getURL().getPath();
|
||||
|
||||
testInvalidPath("file:" + secretPath, handler);
|
||||
testInvalidPath("/file:" + secretPath, handler);
|
||||
testInvalidPath("url:" + secretPath, handler);
|
||||
testInvalidPath("/url:" + secretPath, handler);
|
||||
testInvalidPath("/../.." + secretPath, handler);
|
||||
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
|
||||
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
|
||||
testInvalidPath("%2F%2F%2E%2E%2F%2F%2E%2E" + secretPath, handler);
|
||||
}
|
||||
|
||||
private void testInvalidPath(String requestPath, ResourceWebHandler handler) {
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, requestPath);
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("httpMethods")
|
||||
void resolvePathWithTraversal(HttpMethod method) throws Exception {
|
||||
Resource location = new ClassPathResource("test/", getClass());
|
||||
this.handler.setLocations(List.of(location));
|
||||
|
||||
testResolvePathWithTraversal(method, "../testsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, "test/../../testsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, ":/../../testsecret/secret.txt", location);
|
||||
|
||||
location = new UrlResource(getClass().getResource("./test/"));
|
||||
this.handler.setLocations(List.of(location));
|
||||
Resource secretResource = new UrlResource(getClass().getResource("testsecret/secret.txt"));
|
||||
String secretPath = secretResource.getURL().getPath();
|
||||
|
||||
testResolvePathWithTraversal(method, "file:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "/file:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "url:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "/url:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "////../.." + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "/%2E%2E/testsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, "url:" + secretPath, location);
|
||||
|
||||
// The following tests fail with a MalformedURLException on Windows
|
||||
// testResolvePathWithTraversal(location, "/" + secretPath);
|
||||
// testResolvePathWithTraversal(location, "/ " + secretPath);
|
||||
}
|
||||
|
||||
private void testResolvePathWithTraversal(HttpMethod httpMethod, String requestPath, Resource location)
|
||||
throws Exception {
|
||||
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.method(httpMethod, ""));
|
||||
setPathWithinHandlerMapping(exchange, requestPath);
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
})
|
||||
.verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void processPath() {
|
||||
assertThat(this.handler.processPath("/foo/bar")).isSameAs("/foo/bar");
|
||||
assertThat(this.handler.processPath("foo/bar")).isSameAs("foo/bar");
|
||||
|
||||
// leading whitespace control characters (00-1F)
|
||||
assertThat(this.handler.processPath(" /foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 1 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 31 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath(" foo/bar")).isEqualTo("foo/bar");
|
||||
assertThat(this.handler.processPath((char) 31 + "foo/bar")).isEqualTo("foo/bar");
|
||||
|
||||
// leading control character 0x7F (DEL)
|
||||
assertThat(this.handler.processPath((char) 127 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 127 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
|
||||
// leading control and '/' characters
|
||||
assertThat(this.handler.processPath(" / foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath(" / / foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath(" // /// //// foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 1 + " / " + (char) 127 + " // foo/bar")).isEqualTo("/foo/bar");
|
||||
|
||||
// root or empty path
|
||||
assertThat(this.handler.processPath(" ")).isEmpty();
|
||||
assertThat(this.handler.processPath("/")).isEqualTo("/");
|
||||
assertThat(this.handler.processPath("///")).isEqualTo("/");
|
||||
assertThat(this.handler.processPath("/ / / ")).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
void initAllowedLocations() {
|
||||
PathResourceResolver resolver = (PathResourceResolver) this.handler.getResourceResolvers().get(0);
|
||||
Resource[] locations = resolver.getAllowedLocations();
|
||||
|
||||
assertThat(locations).containsExactly(this.testResource, this.testAlternatePathResource, this.webjarsResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void initAllowedLocationsWithExplicitConfiguration() throws Exception {
|
||||
ClassPathResource location1 = new ClassPathResource("test/", getClass());
|
||||
ClassPathResource location2 = new ClassPathResource("testalternatepath/", getClass());
|
||||
|
||||
PathResourceResolver pathResolver = new PathResourceResolver();
|
||||
pathResolver.setAllowedLocations(location1);
|
||||
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setResourceResolvers(List.of(pathResolver));
|
||||
handler.setLocations(List.of(location1, location2));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
assertThat(pathResolver.getAllowedLocations()).containsExactly(location1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void notModified() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("").ifModifiedSince(resourceLastModified("test/foo.css")));
|
||||
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.NOT_MODIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void modified() throws Exception {
|
||||
long timestamp = resourceLastModified("test/foo.css") / 1000 * 1000 - 1;
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("").ifModifiedSince(timestamp).build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat((Object) exchange.getResponse().getStatusCode()).isNull();
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void directory() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "js/");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void directoryInJarFile() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "underscorejs/");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingResourcePath() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noPathWithinHandlerMappingAttribute() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
this.handler.handle(exchange).block(TIMEOUT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedHttpMethod() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(""));
|
||||
|
|
@ -563,6 +223,37 @@ class ResourceWebHandlerTests {
|
|||
StepVerifier.create(mono).consumeErrorWith(ex -> assertThat(ex).isNotSameAs(exceptionRef.get())).verify();
|
||||
}
|
||||
|
||||
static Stream<HttpMethod> httpMethods() {
|
||||
return Arrays.stream(HttpMethod.values());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class RangeRequestTests {
|
||||
|
||||
private ResourceWebHandler handler;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
this.handler = new ResourceWebHandler();
|
||||
this.handler.setLocations(List.of(testResource, testAlternatePathResource, webjarsResource));
|
||||
this.handler.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
void supportsRangeRequest() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getFirst("Accept-Ranges")).isEqualTo("bytes");
|
||||
assertThat(headers.get("Accept-Ranges")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void partialContentByteRange() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("").header("Range", "bytes=0-1").build();
|
||||
|
|
@ -709,8 +400,110 @@ class ResourceWebHandlerTests {
|
|||
.verify();
|
||||
}
|
||||
|
||||
@Test // SPR-14005
|
||||
void doOverwriteExistingCacheControlHeaders() {
|
||||
}
|
||||
|
||||
@Nested
|
||||
class HttpCachingTests {
|
||||
|
||||
private ResourceWebHandler handler;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.handler = new ResourceWebHandler();
|
||||
this.handler.setLocations(List.of(testResource, testAlternatePathResource, webjarsResource));
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultCachingHeaders() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(headers.getLastModified() / 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureCacheSeconds() throws Exception {
|
||||
this.handler.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS));
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getCacheControl()).isEqualTo("max-age=3600");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureCacheSecondsToZero() throws Exception {
|
||||
this.handler.setCacheControl(CacheControl.noStore());
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.setCacheControl(CacheControl.noStore());
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
MockServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().getCacheControl()).isEqualTo("no-store");
|
||||
assertThat(response.getHeaders().containsKey("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(response.getHeaders().getLastModified() / 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureVersionResourceResolver() throws Exception {
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.addFixedVersionStrategy("versionString", "/**");
|
||||
this.handler.setResourceResolvers(List.of(versionResolver, new PathResourceResolver()));
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "versionString/foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat(exchange.getResponse().getHeaders().getETag()).isEqualTo("W/\"versionString\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespondWithNotModifiedWhenModifiedSince() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("").ifModifiedSince(resourceLastModified("test/foo.css")));
|
||||
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.NOT_MODIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespondWithModifiedResource() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
long timestamp = resourceLastModified("test/foo.css") / 1000 * 1000 - 1;
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("").ifModifiedSince(timestamp).build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat((Object) exchange.getResponse().getStatusCode()).isNull();
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test
|
||||
// SPR-14005
|
||||
void doOverwriteExistingCacheControlHeaders() throws Exception {
|
||||
this.handler.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS));
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
exchange.getResponse().getHeaders().setCacheControl(CacheControl.noStore().getHeaderValue());
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
|
|
@ -721,7 +514,8 @@ class ResourceWebHandlerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void ignoreLastModified() {
|
||||
void ignoreLastModified() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "foo.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
|
|
@ -735,6 +529,283 @@ class ResourceWebHandlerTests {
|
|||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class ResourceLocationTests {
|
||||
|
||||
private ResourceWebHandler handler;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
this.handler = new ResourceWebHandler();
|
||||
this.handler.setLocations(List.of(testResource, testAlternatePathResource, webjarsResource));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromAlternatePath() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "baz.css");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromSubDirectory() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "js/foo.js");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
assertThat(exchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.parseMediaType("application/javascript"));
|
||||
assertResponseBody(exchange, "function foo() { console.log(\"hello world\"); }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFromSubDirectoryOfAlternatePath() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "js/baz.js");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("application/javascript"));
|
||||
assertResponseBody(exchange, "function foo() { console.log(\"hello world\"); }");
|
||||
}
|
||||
|
||||
@Test
|
||||
// gh-27538, gh-27624
|
||||
void filterNonExistingLocations() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setLocations(List.of(testResource, testAlternatePathResource, new ClassPathResource("nosuchpath/", getClass())));
|
||||
handler.setOptimizeLocations(true);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
List<Resource> actual = handler.getLocations();
|
||||
assertThat(actual).hasSize(2);
|
||||
assertThat(actual.get(0).getURL().toString()).endsWith("test/");
|
||||
assertThat(actual.get(1).getURL().toString()).endsWith("testalternatepath/");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectInvalidPath() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
// Use mock ResourceResolver: i.e. we're only testing upfront validations...
|
||||
|
||||
Resource resource = mock();
|
||||
given(resource.getFilename()).willThrow(new AssertionError("Resource should not be resolved"));
|
||||
given(resource.getInputStream()).willThrow(new AssertionError("Resource should not be resolved"));
|
||||
ResourceResolver resolver = mock();
|
||||
given(resolver.resolveResource(any(), any(), any(), any())).willReturn(Mono.just(resource));
|
||||
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setLocations(List.of(new ClassPathResource("test/", getClass())));
|
||||
handler.setResourceResolvers(List.of(resolver));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
testInvalidPath("../testsecret/secret.txt", handler);
|
||||
testInvalidPath("test/../../testsecret/secret.txt", handler);
|
||||
testInvalidPath(":/../../testsecret/secret.txt", handler);
|
||||
|
||||
Resource location = new UrlResource(getClass().getResource("./test/"));
|
||||
handler.setLocations(List.of(location));
|
||||
Resource secretResource = new UrlResource(getClass().getResource("testsecret/secret.txt"));
|
||||
String secretPath = secretResource.getURL().getPath();
|
||||
|
||||
testInvalidPath("file:" + secretPath, handler);
|
||||
testInvalidPath("/file:" + secretPath, handler);
|
||||
testInvalidPath("url:" + secretPath, handler);
|
||||
testInvalidPath("/url:" + secretPath, handler);
|
||||
testInvalidPath("/../.." + secretPath, handler);
|
||||
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
|
||||
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
|
||||
testInvalidPath("%2F%2F%2E%2E%2F%2F%2E%2E" + secretPath, handler);
|
||||
}
|
||||
|
||||
private void testInvalidPath(String requestPath, ResourceWebHandler handler) {
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, requestPath);
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("httpMethods")
|
||||
void resolvePathWithTraversal(HttpMethod method) throws Exception {
|
||||
Resource location = new ClassPathResource("test/", getClass());
|
||||
this.handler.setLocations(List.of(location));
|
||||
|
||||
testResolvePathWithTraversal(method, "../testsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, "test/../../testsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, ":/../../testsecret/secret.txt", location);
|
||||
|
||||
location = new UrlResource(getClass().getResource("./test/"));
|
||||
this.handler.setLocations(List.of(location));
|
||||
Resource secretResource = new UrlResource(getClass().getResource("testsecret/secret.txt"));
|
||||
String secretPath = secretResource.getURL().getPath();
|
||||
|
||||
testResolvePathWithTraversal(method, "file:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "/file:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "url:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "/url:" + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "////../.." + secretPath, location);
|
||||
testResolvePathWithTraversal(method, "/%2E%2E/testsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt", location);
|
||||
testResolvePathWithTraversal(method, "url:" + secretPath, location);
|
||||
|
||||
// The following tests fail with a MalformedURLException on Windows
|
||||
// testResolvePathWithTraversal(location, "/" + secretPath);
|
||||
// testResolvePathWithTraversal(location, "/ " + secretPath);
|
||||
}
|
||||
|
||||
static Stream<HttpMethod> httpMethods() {
|
||||
return Arrays.stream(HttpMethod.values());
|
||||
}
|
||||
|
||||
private void testResolvePathWithTraversal(HttpMethod httpMethod, String requestPath, Resource location) {
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.method(httpMethod, ""));
|
||||
setPathWithinHandlerMapping(exchange, requestPath);
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
})
|
||||
.verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void processPath() {
|
||||
assertThat(this.handler.processPath("/foo/bar")).isSameAs("/foo/bar");
|
||||
assertThat(this.handler.processPath("foo/bar")).isSameAs("foo/bar");
|
||||
|
||||
// leading whitespace control characters (00-1F)
|
||||
assertThat(this.handler.processPath(" /foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 1 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 31 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath(" foo/bar")).isEqualTo("foo/bar");
|
||||
assertThat(this.handler.processPath((char) 31 + "foo/bar")).isEqualTo("foo/bar");
|
||||
|
||||
// leading control character 0x7F (DEL)
|
||||
assertThat(this.handler.processPath((char) 127 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 127 + "/foo/bar")).isEqualTo("/foo/bar");
|
||||
|
||||
// leading control and '/' characters
|
||||
assertThat(this.handler.processPath(" / foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath(" / / foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath(" // /// //// foo/bar")).isEqualTo("/foo/bar");
|
||||
assertThat(this.handler.processPath((char) 1 + " / " + (char) 127 + " // foo/bar")).isEqualTo("/foo/bar");
|
||||
|
||||
// root or empty path
|
||||
assertThat(this.handler.processPath(" ")).isEmpty();
|
||||
assertThat(this.handler.processPath("/")).isEqualTo("/");
|
||||
assertThat(this.handler.processPath("///")).isEqualTo("/");
|
||||
assertThat(this.handler.processPath("/ / / ")).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
void initAllowedLocations() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
PathResourceResolver resolver = (PathResourceResolver) this.handler.getResourceResolvers().get(0);
|
||||
Resource[] locations = resolver.getAllowedLocations();
|
||||
|
||||
assertThat(locations).containsExactly(testResource, testAlternatePathResource, webjarsResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void initAllowedLocationsWithExplicitConfiguration() throws Exception {
|
||||
PathResourceResolver pathResolver = new PathResourceResolver();
|
||||
pathResolver.setAllowedLocations(testResource);
|
||||
this.handler.setResourceResolvers(List.of(pathResolver));
|
||||
this.handler.setLocations(List.of(testResource, testAlternatePathResource));
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
assertThat(pathResolver.getAllowedLocations()).containsExactly(testResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotServeDirectory() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "js/");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotServeDirectoryInJarFile() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "underscorejs/");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void servesResourcesFromFileSystem() throws Exception {
|
||||
String packagePath = ClassUtils.classPackageAsResourcePath(getClass());
|
||||
String path = Paths.get("src/test/resources", packagePath).normalize() + "/";
|
||||
|
||||
this.handler.setLocations(List.of(new FileSystemResource(path)));
|
||||
this.handler.afterPropertiesSet();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, UriUtils.encodePath("test/foo with spaces.css", UTF_8));
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotServeMissingResourcePath() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
setPathWithinHandlerMapping(exchange, "");
|
||||
setBestMachingPattern(exchange, "/**");
|
||||
StepVerifier.create(this.handler.handle(exchange))
|
||||
.expectErrorSatisfies(err -> {
|
||||
assertThat(err).isInstanceOf(ResponseStatusException.class);
|
||||
assertThat(((ResponseStatusException) err).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}).verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noPathWithinHandlerMappingAttribute() throws Exception {
|
||||
this.handler.afterPropertiesSet();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(""));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
this.handler.handle(exchange).block(TIMEOUT));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void setPathWithinHandlerMapping(ServerWebExchange exchange, String path) {
|
||||
exchange.getAttributes().put(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
|
||||
|
|
@ -761,9 +832,8 @@ class ResourceWebHandlerTests {
|
|||
.verify();
|
||||
}
|
||||
|
||||
|
||||
static Stream<HttpMethod> httpMethods() {
|
||||
return Arrays.stream(HttpMethod.values());
|
||||
private void assertResponseBodyIsEmpty(MockServerWebExchange exchange) {
|
||||
StepVerifier.create(exchange.getResponse().getBody()).verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue