/** * Returns a {@link ResourceWebHandler} instance. */ protected ResourceWebHandler getRequestHandler() { ResourceWebHandler handler = new ResourceWebHandler(); handler.setLocationValues(this.locationValues); handler.setResourceLoader(this.resourceLoader); if (this.resourceChainRegistration != null) { handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers()); handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers()); } if (this.cacheControl != null) { handler.setCacheControl(this.cacheControl); } return handler; }
@Test public 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(Collections.singletonList(pathResolver)); handler.setLocations(Arrays.asList(location1, location2)); handler.afterPropertiesSet(); Resource[] locations = pathResolver.getAllowedLocations(); assertEquals(1, locations.length); assertEquals("test/", ((ClassPathResource) locations[0]).getPath()); }
/** * Look for a {@code PathResourceResolver} among the configured resource * resolvers and set its {@code allowedLocations} property (if empty) to * match the {@link #setLocations locations} configured on this class. */ protected void initAllowedLocations() { if (CollectionUtils.isEmpty(this.locations)) { if (logger.isInfoEnabled()) { logger.info("Locations list is empty. No resources will be served unless a " + "custom ResourceResolver is configured as an alternative to PathResourceResolver."); } return; } for (int i = getResourceResolvers().size() - 1; i >= 0; i--) { if (getResourceResolvers().get(i) instanceof PathResourceResolver) { PathResourceResolver resolver = (PathResourceResolver) getResourceResolvers().get(i); if (ObjectUtils.isEmpty(resolver.getAllowedLocations())) { resolver.setAllowedLocations(getLocations().toArray(new Resource[0])); } break; } } }
/** * Check whether the given path contains invalid escape sequences. * @param path the path to validate * @return {@code true} if the path is invalid, {@code false} otherwise */ private boolean isInvalidEncodedPath(String path) { if (path.contains("%")) { try { // Use URLDecoder (vs UriUtils) to preserve potentially decoded UTF-8 chars String decodedPath = URLDecoder.decode(path, "UTF-8"); if (isInvalidPath(decodedPath)) { return true; } decodedPath = processPath(decodedPath); if (isInvalidPath(decodedPath)) { return true; } } catch (IllegalArgumentException | UnsupportedEncodingException ex) { // Should never happen... } } return false; }
protected Mono<Resource> getResource(ServerWebExchange exchange) { String name = HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE; PathContainer pathWithinHandler = exchange.getRequiredAttribute(name); String path = processPath(pathWithinHandler.value()); if (!StringUtils.hasText(path) || isInvalidPath(path)) { return Mono.empty(); } if (isInvalidEncodedPath(path)) { return Mono.empty(); } Assert.state(this.resolverChain != null, "ResourceResolverChain not initialized"); Assert.state(this.transformerChain != null, "ResourceTransformerChain not initialized"); return this.resolverChain.resolveResource(exchange, path, getLocations()) .flatMap(resource -> this.transformerChain.transform(exchange, resource)); }
private ResourceUrlProvider createUrlProvider(List<ResourceResolver> resolvers) { ResourceWebHandler handler = new ResourceWebHandler(); handler.setLocations(Collections.singletonList(new ClassPathResource("test/", getClass()))); handler.setResourceResolvers(resolvers); ResourceUrlProvider urlProvider = new ResourceUrlProvider(); urlProvider.registerHandlers(Collections.singletonMap("/resources/**", handler)); return urlProvider; }
@Before public void setup() throws Exception { List<Resource> locations = new ArrayList<>(2); locations.add(new ClassPathResource("test/", getClass())); locations.add(new ClassPathResource("testalternatepath/", getClass())); locations.add(new ClassPathResource("META-INF/resources/webjars/")); this.handler = new ResourceWebHandler(); this.handler.setLocations(locations); this.handler.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS)); this.handler.afterPropertiesSet(); }
/** * Returns a {@link ResourceWebHandler} instance. */ protected ResourceWebHandler getRequestHandler() { ResourceWebHandler handler = new ResourceWebHandler(); if (this.resourceChainRegistration != null) { handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers()); handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers()); } handler.setLocations(this.locations); if (this.cacheControl != null) { handler.setCacheControl(this.cacheControl); } return handler; }
@Test public void resourceChainWithoutCaching() throws Exception { this.registration.resourceChain(false); ResourceWebHandler handler = getHandler("/resources/**"); List<ResourceResolver> resolvers = handler.getResourceResolvers(); assertThat(resolvers, Matchers.hasSize(2)); assertThat(resolvers.get(0), Matchers.instanceOf(WebJarsResourceResolver.class)); assertThat(resolvers.get(1), Matchers.instanceOf(PathResourceResolver.class)); List<ResourceTransformer> transformers = handler.getResourceTransformers(); assertThat(transformers, Matchers.hasSize(0)); }
@Test // SPR-14577 public void getMediaTypeWithFavorPathExtensionOff() throws Exception { List<Resource> paths = Collections.singletonList(new ClassPathResource("test/", getClass())); ResourceWebHandler handler = new ResourceWebHandler(); handler.setLocations(paths); handler.afterPropertiesSet(); MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("") .header("Accept", "application/json,text/plain,*/*")); setPathWithinHandlerMapping(exchange, "foo.html"); handler.handle(exchange).block(TIMEOUT); assertEquals(MediaType.TEXT_HTML, exchange.getResponse().getHeaders().getContentType()); }
String path = processPath(optional.get()); if (!StringUtils.hasText(path) || isInvalidPath(path)) { if (logger.isTraceEnabled()) { logger.trace("Ignoring invalid resource path [" + path + "]"); try { if (isInvalidPath(URLDecoder.decode(path, "UTF-8"))) { if (logger.isTraceEnabled()) { logger.trace("Ignoring invalid resource path with escape sequences [" + path + "]."); ResourceResolverChain resolveChain = createResolverChain(); return resolveChain.resolveResource(exchange, path, getLocations()) .then(resource -> { ResourceTransformerChain transformerChain = createTransformerChain(resolveChain); return transformerChain.transform(exchange, resource); });
return getResource(exchange) .switchIfEmpty(Mono.defer(() -> { logger.debug(exchange.getLogPrefix() + "Resource not found"); CacheControl cacheControl = getCacheControl(); if (cacheControl != null) { exchange.getResponse().getHeaders().setCacheControl(cacheControl); setHeaders(exchange, resource, mediaType); exchange.getResponse().getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); return Mono.empty(); setHeaders(exchange, resource, mediaType); ResourceHttpMessageWriter writer = getResourceHttpMessageWriter(); Assert.state(writer != null, "No ResourceHttpMessageWriter"); return writer.write(Mono.just(resource),
public Mono<Void> handle(ServerWebExchange exchange) { return getResource(exchange) .otherwiseIfEmpty(Mono.defer(() -> { logger.trace("No matching resource found - returning 404"); if (getCacheControl() != null) { String value = getCacheControl().getHeaderValue(); if (value != null) { exchange.getResponse().getHeaders().setCacheControl(value); MediaType mediaType = getMediaType(exchange, resource); if (mediaType != null) { if (logger.isTraceEnabled()) { setHeaders(exchange, resource, mediaType); exchange.getResponse().getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); logger.trace("HEAD request - skipping content"); setHeaders(exchange, resource, mediaType); return this.resourceHttpMessageWriter.write(Mono.just(resource), null, ResolvableType.forClass(Resource.class), mediaType,
@Test public void getVersionedResource() throws Exception { VersionResourceResolver versionResolver = new VersionResourceResolver(); versionResolver.addFixedVersionStrategy("versionString", "/**"); this.handler.setResourceResolvers(Arrays.asList(versionResolver, new PathResourceResolver())); this.handler.afterPropertiesSet(); MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("")); setPathWithinHandlerMapping(exchange, "versionString/foo.css"); this.handler.handle(exchange).block(TIMEOUT); assertEquals("\"versionString\"", exchange.getResponse().getHeaders().getETag()); assertEquals("bytes", exchange.getResponse().getHeaders().getFirst("Accept-Ranges")); assertEquals(1, exchange.getResponse().getHeaders().get("Accept-Ranges").size()); }
for (String pathPattern : registration.getPathPatterns()) { ResourceWebHandler handler = registration.getRequestHandler(); handler.getResourceTransformers().forEach(transformer -> { if (transformer instanceof ResourceTransformerSupport) { ((ResourceTransformerSupport) transformer).setResourceUrlProvider(this.resourceUrlProvider); handler.afterPropertiesSet();
@Before public void setup() throws Exception { this.locations.add(new ClassPathResource("test/", getClass())); this.locations.add(new ClassPathResource("testalternatepath/", getClass())); this.handler.setLocations(this.locations); this.handler.afterPropertiesSet(); this.handlerMap.put("/resources/**", this.handler); this.urlProvider.registerHandlers(this.handlerMap); }
/** * Return a handler mapping with the mapped resource handlers; or {@code null} in case * of no registrations. */ protected AbstractHandlerMapping getHandlerMapping() { if (this.registrations.isEmpty()) { return null; } Map<String, WebHandler> urlMap = new LinkedHashMap<>(); for (ResourceHandlerRegistration registration : this.registrations) { for (String pathPattern : registration.getPathPatterns()) { ResourceWebHandler handler = registration.getRequestHandler(); handler.setContentTypeResolver(this.contentTypeResolver); try { handler.afterPropertiesSet(); handler.afterSingletonsInstantiated(); } catch (Exception ex) { throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex); } urlMap.put(pathPattern, handler); } } SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping(); handlerMapping.setOrder(this.order); handlerMapping.setUrlMap(urlMap); return handlerMapping; }
@Test public void initAllowedLocations() { PathResourceResolver resolver = (PathResourceResolver) this.handler.getResourceResolvers().get(0); Resource[] locations = resolver.getAllowedLocations(); assertEquals(3, locations.length); assertEquals("test/", ((ClassPathResource) locations[0]).getPath()); assertEquals("testalternatepath/", ((ClassPathResource) locations[1]).getPath()); assertEquals("META-INF/resources/webjars/", ((ClassPathResource) locations[2]).getPath()); }
@Test public void getResourceNoCache() throws Exception { MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("")); setPathWithinHandlerMapping(exchange, "foo.css"); this.handler.setCacheControl(CacheControl.noStore()); this.handler.handle(exchange).block(TIMEOUT); MockServerHttpResponse response = exchange.getResponse(); assertEquals("no-store", response.getHeaders().getCacheControl()); assertTrue(response.getHeaders().containsKey("Last-Modified")); assertEquals(response.getHeaders().getLastModified() / 1000, resourceLastModifiedDate("test/foo.css") / 1000); assertEquals("bytes", response.getHeaders().getFirst("Accept-Ranges")); assertEquals(1, response.getHeaders().get("Accept-Ranges").size()); }
@Bean public SimpleUrlHandlerMapping simpleUrlHandlerMapping() { ResourceWebHandler handler = new ResourceWebHandler(); HashMap<String, ResourceWebHandler> handlerMap = new HashMap<>(); handlerMap.put("/resources/**", handler); SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping(); hm.setUrlMap(handlerMap); return hm; }