@@ -5,6 +5,7 @@ import grails.util.Holders
55import groovy.util.logging.Commons
66
77import java.util.concurrent.ConcurrentHashMap
8+ import java.util.concurrent.ConcurrentMap
89
910import javax.servlet.ServletContext
1011import javax.servlet.ServletRequest
@@ -24,6 +25,8 @@ import org.springframework.util.AntPathMatcher
2425import org.springframework.web.context.ServletContextAware
2526import org.springframework.web.util.WebUtils
2627
28+ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap
29+
2730/**
2831 * Primary service facade to actions on resources.
2932 *
@@ -91,11 +94,26 @@ class ResourceProcessor implements InitializingBean, ServletContextAware {
9194 List adHocExcludesLowerCase
9295
9396 List optionalDispositions
97+
98+ boolean resourceLocatorEnabled
99+ boolean serveUnderRootPathOnly
100+
101+ ConcurrentMap<String , Boolean > servingAllowedCache
102+ ConcurrentMap<String , Boolean > resourceAllowedCache
103+
104+ protected ConcurrentLinkedHashMap<String , Boolean > createDefaultAuthorizationCache () {
105+ return new ConcurrentLinkedHashMap.Builder <String , Boolean >()
106+ .maximumWeightedCapacity(5000 )
107+ .build();
108+ }
94109
95110 /**
96111 * Initialize bean after properties have been set.
97112 */
98113 void afterPropertiesSet () {
114+ servingAllowedCache = createDefaultAuthorizationCache()
115+ resourceAllowedCache = createDefaultAuthorizationCache()
116+
99117 processingEnabled = getConfigParamOrDefault(' processing.enabled' , true )
100118 adHocIncludes = getConfigParamOrDefault(' adhoc.includes' , DEFAULT_ADHOC_INCLUDES )
101119 adHocIncludes = adHocIncludes. collect { it. startsWith(' /' ) ? it : ' /' + it }
@@ -111,6 +129,10 @@ class ResourceProcessor implements InitializingBean, ServletContextAware {
111129 optionalDispositions = getConfigParamOrDefault(' optional.dispositions' , [' inline' , ' image' ])
112130
113131 rootUrlNormalized = urlToNormalizedFormat(resolveUriToURL(' /' ))
132+
133+ boolean developmentMode = Environment . getCurrent(). isDevelopmentMode()
134+ resourceLocatorEnabled = getConfigParamOrDefault(' resourceLocatorEnabled' , developmentMode)
135+ serveUnderRootPathOnly = getConfigParamOrDefault(' serveUnderRootPathOnly' , (resourceLocatorEnabled== false ))
114136 }
115137
116138 /**
@@ -206,6 +228,15 @@ class ResourceProcessor implements InitializingBean, ServletContextAware {
206228 }
207229
208230 boolean canProcessLegacyResource (uri ) {
231+ Boolean result = resourceAllowedCache. get(uri)
232+ if (result == null ) {
233+ result = doCanProcessLegacyResource(uri)
234+ resourceAllowedCache. put(uri, result)
235+ }
236+ result
237+ }
238+
239+ boolean doCanProcessLegacyResource (uri ) {
209240 // Apply our own url filtering rules because servlet mapping uris are too lame
210241 boolean included = adHocIncludes. find { p ->
211242 PATH_MATCHER . match(p, uri)
@@ -221,16 +252,29 @@ class ResourceProcessor implements InitializingBean, ServletContextAware {
221252 return included
222253 }
223254
224- boolean isServingURLAllowed (URL url ) {
255+ boolean isServingURLAllowed (String uri , URL url ) {
256+ Boolean result = servingAllowedCache. get(uri)
257+ if (result == null ) {
258+ result = doIsServingURLAllowed(uri, url)
259+ servingAllowedCache. put(uri, result)
260+ }
261+ result
262+ }
263+
264+ boolean doIsServingURLAllowed (String uri , URL url ) {
225265 if (url == null ) return null
226- String urlAsString = urlToNormalizedFormat(url)
227- if (urlAsString== null || rootUrlNormalized == null || ! urlAsString. startsWith(rootUrlNormalized)) {
228- return false ;
266+ if (serveUnderRootPathOnly) {
267+ String urlAsString = urlToNormalizedFormat(url)
268+ if (urlAsString== null || rootUrlNormalized == null || ! urlAsString. startsWith(rootUrlNormalized)) {
269+ return false
270+ }
271+ String relativePath = urlAsString. substring(rootUrlNormalized. length()-1 )
272+ return canProcessLegacyResource(relativePath)
273+ } else {
274+ return canProcessLegacyResource(uri)
229275 }
230- String relativePath = urlAsString. substring(rootUrlNormalized. length()-1 )
231- return canProcessLegacyResource(relativePath)
232276 }
233-
277+
234278 static String urlToNormalizedFormat (URL url ) {
235279 url != null ? url. toURI(). normalize(). toASCIIString() : null
236280 }
@@ -530,7 +574,7 @@ class ResourceProcessor implements InitializingBean, ServletContextAware {
530574 if (url == null ) {
531575 return null
532576 }
533- if (isServingURLAllowed(url)) {
577+ if (isServingURLAllowed(uri, url)) {
534578 return url
535579 } else {
536580 log. warn(" Serving url ${ url} isn't allowed." )
@@ -540,7 +584,7 @@ class ResourceProcessor implements InitializingBean, ServletContextAware {
540584
541585 private URL resolveUriToURL (uri ) {
542586 URL url = null
543- if (grailsResourceLocator != null ) {
587+ if (resourceLocatorEnabled && grailsResourceLocator != null ) {
544588 def res = grailsResourceLocator. findResourceForURI(uri)
545589 if (res != null ) {
546590 url = res.URL
0 commit comments