Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/jmeter.properties
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,9 @@ beanshell.server.file=../extras/startup.bsh
# ORO PatternCacheLRU size
#oro.patterncache.size=1000

# CSS parser LRU cache size
#css.parser.cache.size=400

#TestBeanGui
#
#propertyEditorSearchPath=null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
Expand All @@ -50,7 +54,16 @@
public class CssParser implements LinkExtractorParser {
private static final boolean IGNORE_UNRECOVERABLE_PARSING_ERROR = JMeterUtils.getPropDefault("httpsampler.ignore_failed_embedded_resource", false); //$NON-NLS-1$
private static final Logger LOG = LoggingManager.getLoggerForClass();

private static final int CSS_URL_CACHE_MAX_SIZE = JMeterUtils.getPropDefault("css.parser.cache.size", 0);
private static Map<String, URLCollection> CSS_URL_CACHE = null;

static {
if(CSS_URL_CACHE_MAX_SIZE > 0) {
CSS_URL_CACHE = Collections.synchronizedMap(new LRUMap(CSS_URL_CACHE_MAX_SIZE));
}
}

private static final class CustomLoggingCSSParseExceptionCallback extends LoggingCSSParseExceptionCallback {
/**
*
Expand All @@ -76,6 +89,7 @@ public void onException(ParseException ex) {
}
}
}

/**
*
*/
Expand All @@ -93,40 +107,55 @@ public CssParser() {
public Iterator<URL> getEmbeddedResourceURLs(String userAgent, byte[] data,
final URL baseUrl, String encoding) throws LinkExtractorParseException {
try {
String cssContent = new String(data, encoding);
final CascadingStyleSheet aCSS = CSSReader.readFromStringStream(cssContent,
new CSSReaderSettings()
.setBrowserCompliantMode(true)
.setFallbackCharset(Charset.forName(encoding))
.setCSSVersion (ECSSVersion.CSS30)
.setCustomErrorHandler(new LoggingCSSParseErrorHandler())
.setCustomExceptionHandler (new CustomLoggingCSSParseExceptionCallback(baseUrl)));
final List<URLString> list = new ArrayList<>();
final URLCollection urlCollection = new URLCollection(list);
if(aCSS != null) {
CSSVisitor.visitCSSUrl(aCSS, new DefaultCSSUrlVisitor() {
@Override
public void onImport(final CSSImportRule importRule) {
String location = importRule.getLocationString();
if(!StringUtils.isEmpty(location)) {
urlCollection.addURL(location, baseUrl);
boolean cacheEnabled = CSS_URL_CACHE_MAX_SIZE > 0;
String md5Key = null;
URLCollection urlCollection = null;
if(cacheEnabled) {
md5Key = DigestUtils.md5Hex(data);
urlCollection = CSS_URL_CACHE.get(md5Key);
}

if(urlCollection == null) {
String cssContent = new String(data, encoding);
final CascadingStyleSheet aCSS = CSSReader.readFromStringStream(cssContent,
new CSSReaderSettings()
.setBrowserCompliantMode(true)
.setFallbackCharset(Charset.forName(encoding))
.setCSSVersion (ECSSVersion.CSS30)
.setCustomErrorHandler(new LoggingCSSParseErrorHandler())
.setCustomExceptionHandler (new CustomLoggingCSSParseExceptionCallback(baseUrl)));
final List<URLString> list = new ArrayList<>();
urlCollection = new URLCollection(list);
final URLCollection localCollection = urlCollection;
if(aCSS != null) {
CSSVisitor.visitCSSUrl(aCSS, new DefaultCSSUrlVisitor() {
@Override
public void onImport(final CSSImportRule importRule) {
String location = importRule.getLocationString();
if(!StringUtils.isEmpty(location)) {
localCollection.addURL(location, baseUrl);
}
}
// Call for URLs outside of URLs
@Override
public void onUrlDeclaration(
final ICSSTopLevelRule aTopLevelRule,
final CSSDeclaration aDeclaration,
final CSSExpressionMemberTermURI aURITerm) {
// NOOP
// Browser fetch such urls only when CSS rule matches
// so we disable this code
//urlCollection.addURL(aURITerm.getURIString(), baseUrl);
}
});
if(cacheEnabled) {
CSS_URL_CACHE.put(md5Key, urlCollection);
}
// Call for URLs outside of URLs
@Override
public void onUrlDeclaration(
final ICSSTopLevelRule aTopLevelRule,
final CSSDeclaration aDeclaration,
final CSSExpressionMemberTermURI aURITerm) {
// NOOP
// Browser fetch such urls only when CSS rule matches
// so we disable this code
//urlCollection.addURL(aURITerm.getURIString(), baseUrl);
}
});
} else {
LOG.warn("Failed parsing url:"+baseUrl+", got null CascadingStyleSheet");
} else {
LOG.warn("Failed parsing url:"+baseUrl+", got null CascadingStyleSheet");
}
}

if(LOG.isDebugEnabled()) {
StringBuilder builder = new StringBuilder();
for (Iterator<URL> iterator = urlCollection.iterator(); iterator.hasNext();) {
Expand All @@ -135,6 +164,7 @@ public void onUrlDeclaration(
}
LOG.debug("Parsed:"+baseUrl+", got:"+builder.toString());
}

return urlCollection.iterator();
} catch (Exception e) {
throw new LinkExtractorParseException(e);
Expand Down