diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java b/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java index b848bdfe06e..7510c371776 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java @@ -40,4 +40,18 @@ public class CompressedContentFormat _etagQuote = _etag + "\""; _contentEncoding = new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING, encoding); } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof CompressedContentFormat)) + return false; + CompressedContentFormat ccf = (CompressedContentFormat)o; + if (_encoding==null && ccf._encoding!=null) + return false; + if (_extension==null && ccf._extension!=null) + return false; + + return _encoding.equalsIgnoreCase(ccf._encoding) && _extension.equalsIgnoreCase(ccf._extension); + } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java index 0a2df81bfb4..a5256f91561 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java @@ -36,10 +36,11 @@ public class PrecompressedHttpContent implements HttpContent public PrecompressedHttpContent(HttpContent content, HttpContent precompressedContent, CompressedContentFormat format) { - _content=content; - _precompressedContent=precompressedContent; - _format=format; - if (_precompressedContent == null || _format == null) { + _content = content; + _precompressedContent = precompressedContent; + _format = format; + if (_precompressedContent == null || _format == null) + { throw new NullPointerException("Missing compressed content and/or format"); } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java index 92a5a912d6d..591fc80653c 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java @@ -61,13 +61,17 @@ public class ResourceHttpContent implements HttpContent { _resource=resource; _contentType=contentType; - _maxBuffer=maxBuffer; - if (precompressedContents == null) { + _maxBuffer = maxBuffer; + if (precompressedContents == null) + { _precompressedContents = null; - } else { + } + else + { _precompressedContents = new HashMap<>(precompressedContents.size()); - for (Map.Entry entry : precompressedContents.entrySet()) { - _precompressedContents.put(entry.getKey(), new PrecompressedHttpContent(this, entry.getValue(), entry.getKey())); + for (Map.Entry entry : precompressedContents.entrySet()) + { + _precompressedContents.put(entry.getKey(),new PrecompressedHttpContent(this,entry.getValue(),entry.getKey())); } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java similarity index 88% rename from jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java rename to jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java index d4e19e1c5e8..55818c55eeb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java @@ -49,16 +49,18 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; -// TODO rename to ContentCache -public class ResourceCache implements HttpContent.Factory +/** + * Caching HttpContent.Factory + */ +public class CachedContentFactory implements HttpContent.Factory { - private static final Logger LOG = Log.getLogger(ResourceCache.class); + private static final Logger LOG = Log.getLogger(CachedContentFactory.class); private final ConcurrentMap _cache; private final AtomicInteger _cachedSize; private final AtomicInteger _cachedFiles; private final ResourceFactory _factory; - private final ResourceCache _parent; + private final CachedContentFactory _parent; private final MimeTypes _mimeTypes; private final boolean _etags; private final CompressedContentFormat[] _precompressedFormats; @@ -77,7 +79,7 @@ public class ResourceCache implements HttpContent.Factory * @param etags true to support etags * @param precompressedFormats array of precompression formats to support */ - public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags,CompressedContentFormat[] precompressedFormats) + public CachedContentFactory(CachedContentFactory parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags,CompressedContentFormat[] precompressedFormats) { _factory = factory; _cache=new ConcurrentHashMap(); @@ -237,73 +239,82 @@ public class ResourceCache implements HttpContent.Factory private HttpContent load(String pathInContext, Resource resource, int maxBufferSize) throws IOException { - if (resource==null || !resource.exists()) + if (resource == null || !resource.exists()) return null; - + if (resource.isDirectory()) return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),getMaxCachedFileSize()); - + // Will it fit in the cache? if (isCacheable(resource)) - { - CachedHttpContent content=null; - + { + CachedHttpContent content = null; + // Look for precompressed resources if (_precompressedFormats.length > 0) { - Map precompresssedContents=new HashMap<>(_precompressedFormats.length); - for (CompressedContentFormat format : _precompressedFormats) { - String compressedPathInContext=pathInContext+format._extension; - CachedHttpContent compressedContent=_cache.get(compressedPathInContext); - if (compressedContent==null || compressedContent.isValid()) { - compressedContent=null; + Map precompresssedContents = new HashMap<>(_precompressedFormats.length); + for (CompressedContentFormat format : _precompressedFormats) + { + String compressedPathInContext = pathInContext + format._extension; + CachedHttpContent compressedContent = _cache.get(compressedPathInContext); + if (compressedContent == null || compressedContent.isValid()) + { + compressedContent = null; Resource compressedResource = _factory.getResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified()>=resource.lastModified() && compressedResource.length()= resource.lastModified() + && compressedResource.length() < resource.length()) + { + compressedContent = new CachedHttpContent(compressedPathInContext,compressedResource,null); + CachedHttpContent added = _cache.putIfAbsent(compressedPathInContext,compressedContent); + if (added != null) + { compressedContent.invalidate(); - compressedContent=added; + compressedContent = added; } } } - if (compressedContent!=null) + if (compressedContent != null) precompresssedContents.put(format,compressedContent); } content = new CachedHttpContent(pathInContext,resource,precompresssedContents); } - else + else content = new CachedHttpContent(pathInContext,resource,null); // Add it to the cache. CachedHttpContent added = _cache.putIfAbsent(pathInContext,content); - if (added!=null) + if (added != null) { content.invalidate(); - content=added; + content = added; } - + return content; } - + // Look for non Cacheable precompressed resource or content String mt = _mimeTypes.getMimeByExtension(pathInContext); - if (_precompressedFormats.length>0) { + if (_precompressedFormats.length > 0) + { // Is the precompressed content cached? Map compressedContents = new HashMap<>(); - for (CompressedContentFormat format : _precompressedFormats) { - String compressedPathInContext=pathInContext+format._extension; - CachedHttpContent compressedContent=_cache.get(compressedPathInContext); - if (compressedContent!=null && compressedContent.isValid() && compressedContent.getResource().lastModified()>=resource.lastModified()) + for (CompressedContentFormat format : _precompressedFormats) + { + String compressedPathInContext = pathInContext + format._extension; + CachedHttpContent compressedContent = _cache.get(compressedPathInContext); + if (compressedContent != null && compressedContent.isValid() && compressedContent.getResource().lastModified() >= resource.lastModified()) compressedContents.put(format,compressedContent); // Is there a precompressed resource? - Resource compressedResource=_factory.getResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified()>=resource.lastModified() && compressedResource.length()= resource.lastModified() + && compressedResource.length() < resource.length()) + compressedContents.put(format, + new ResourceHttpContent(compressedResource,_mimeTypes.getMimeByExtension(compressedPathInContext),maxBufferSize)); } if (!compressedContents.isEmpty()) - return new ResourceHttpContent(resource, mt, maxBufferSize, compressedContents); + return new ResourceHttpContent(resource,mt,maxBufferSize,compressedContents); } return new ResourceHttpContent(resource,mt,maxBufferSize); @@ -433,14 +444,18 @@ public class ResourceCache implements HttpContent.Factory _lastAccessed=System.currentTimeMillis(); - _etag=ResourceCache.this._etags?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null; + _etag=CachedContentFactory.this._etags?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null; - if (precompressedResources != null) { + if (precompressedResources != null) + { _precompressed = new HashMap<>(precompressedResources.size()); - for (Map.Entry entry : precompressedResources.entrySet()) { - _precompressed.put(entry.getKey(), new CachedPrecompressedHttpContent(this, entry.getValue(), entry.getKey())); + for (Map.Entry entry : precompressedResources.entrySet()) + { + _precompressed.put(entry.getKey(),new CachedPrecompressedHttpContent(this,entry.getValue(),entry.getKey())); } - } else { + } + else + { _precompressed = null; } } @@ -570,7 +585,6 @@ public class ResourceCache implements HttpContent.Factory return _mimeType; } - /* ------------------------------------------------------------ */ @Override public void release() @@ -584,7 +598,7 @@ public class ResourceCache implements HttpContent.Factory ByteBuffer buffer = _indirectBuffer.get(); if (buffer==null) { - ByteBuffer buffer2=ResourceCache.this.getIndirectBuffer(_resource); + ByteBuffer buffer2=CachedContentFactory.this.getIndirectBuffer(_resource); if (buffer2==null) LOG.warn("Could not load "+this); @@ -609,7 +623,7 @@ public class ResourceCache implements HttpContent.Factory ByteBuffer buffer = _directBuffer.get(); if (buffer==null) { - ByteBuffer buffer2=ResourceCache.this.getDirectBuffer(_resource); + ByteBuffer buffer2=CachedContentFactory.this.getDirectBuffer(_resource); if (buffer2==null) LOG.warn("Could not load "+this); @@ -676,7 +690,8 @@ public class ResourceCache implements HttpContent.Factory Map ret=_precompressed; for (Map.Entry entry:_precompressed.entrySet()) { - if (!entry.getValue().isValid()) { + if (!entry.getValue().isValid()) + { if (ret == _precompressed) ret = new HashMap<>(_precompressed); ret.remove(entry.getKey()); @@ -701,7 +716,7 @@ public class ResourceCache implements HttpContent.Factory _content=content; _precompressedContent=precompressedContent; - _etag=(ResourceCache.this._etags)?new PreEncodedHttpField(HttpHeader.ETAG,_content.getResource().getWeakETag(format._etag)):null; + _etag=(CachedContentFactory.this._etags)?new PreEncodedHttpField(HttpHeader.ETAG,_content.getResource().getWeakETag(format._etag)):null; } public boolean isValid() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java index bf418daf76c..33a79ab7094 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java @@ -32,7 +32,7 @@ import org.eclipse.jetty.util.resource.ResourceFactory; /** - * A HttpContent.Factory for transient content. The HttpContent's created by + * A HttpContent.Factory for transient content (not cached). The HttpContent's created by * this factory are not intended to be cached, so memory limits for individual * HttpOutput streams are enforced. */ @@ -71,17 +71,21 @@ public class ResourceContentFactory implements Factory if (resource.isDirectory()) return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),maxBufferSize); - + // Look for a precompressed resource or content String mt = _mimeTypes.getMimeByExtension(pathInContext); - if (_precompressedFormats.length>0) { + if (_precompressedFormats.length > 0) + { // Is there a compressed resource? - Map compressedContents = new HashMap<>(_precompressedFormats.length); - for (CompressedContentFormat format:_precompressedFormats) { - String compressedPathInContext=pathInContext+format._extension; - Resource compressedResource=_factory.getResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified()>=resource.lastModified() && compressedResource.length() compressedContents = new HashMap<>(_precompressedFormats.length); + for (CompressedContentFormat format : _precompressedFormats) + { + String compressedPathInContext = pathInContext + format._extension; + Resource compressedResource = _factory.getResource(compressedPathInContext); + if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() + && compressedResource.length() < resource.length()) + compressedContents.put(format, + new ResourceHttpContent(compressedResource,_mimeTypes.getMimeByExtension(compressedPathInContext),maxBufferSize)); } if (!compressedContents.isEmpty()) return new ResourceHttpContent(resource,mt,maxBufferSize,compressedContents); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java index 082be47b07a..7aef29bab8b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java @@ -285,7 +285,8 @@ public abstract class ResourceService } } - private CompressedContentFormat getBestPrecompressedContent(List preferredEncodings, Collection availableFormats) { + private CompressedContentFormat getBestPrecompressedContent(List preferredEncodings, Collection availableFormats) + { if (availableFormats.isEmpty()) return null; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index 6a0bb0bb197..4a8b9ec155a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -322,10 +322,10 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory @Deprecated public boolean isGzip() { - for (CompressedContentFormat formats : _resourceService.getPrecompressedFormats()) { - if (CompressedContentFormat.GZIP._encoding.equals(formats._encoding)) { + for (CompressedContentFormat formats : _resourceService.getPrecompressedFormats()) + { + if (CompressedContentFormat.GZIP._encoding.equals(formats._encoding)) return true; - } } return false; } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java index 6c364e60c66..0133cc28039 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java @@ -51,9 +51,9 @@ public class ResourceCacheTest Resource[] r = rc.getResources(); MimeTypes mime = new MimeTypes(); - ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false,CompressedContentFormat.NONE); - ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false,CompressedContentFormat.NONE); - ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false,CompressedContentFormat.NONE); + CachedContentFactory rc3 = new CachedContentFactory(null,r[2],mime,false,false,CompressedContentFormat.NONE); + CachedContentFactory rc2 = new CachedContentFactory(rc3,r[1],mime,false,false,CompressedContentFormat.NONE); + CachedContentFactory rc1 = new CachedContentFactory(rc2,r[0],mime,false,false,CompressedContentFormat.NONE); assertEquals("1 - one", getContent(rc1, "1.txt")); assertEquals("2 - two", getContent(rc1, "2.txt")); @@ -81,8 +81,8 @@ public class ResourceCacheTest Resource[] r = rc.getResources(); MimeTypes mime = new MimeTypes(); - ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false,CompressedContentFormat.NONE); - ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false,CompressedContentFormat.NONE) + CachedContentFactory rc3 = new CachedContentFactory(null,r[2],mime,false,false,CompressedContentFormat.NONE); + CachedContentFactory rc2 = new CachedContentFactory(rc3,r[1],mime,false,false,CompressedContentFormat.NONE) { @Override public boolean isCacheable(Resource resource) @@ -91,7 +91,7 @@ public class ResourceCacheTest } }; - ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false,CompressedContentFormat.NONE); + CachedContentFactory rc1 = new CachedContentFactory(rc2,r[0],mime,false,false,CompressedContentFormat.NONE); assertEquals("1 - one", getContent(rc1, "1.txt")); assertEquals("2 - two", getContent(rc1, "2.txt")); @@ -114,7 +114,7 @@ public class ResourceCacheTest final Resource directory; File[] files=new File[10]; String[] names=new String[files.length]; - ResourceCache cache; + CachedContentFactory cache; for (int i=0;i @@ -132,7 +139,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory private boolean _welcomeExactServlets=false; private Resource _resourceBase; - private ResourceCache _cache; + private CachedContentFactory _cache; private MimeTypes _mimeTypes; private String[] _welcomes; @@ -264,14 +271,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory LOG.debug("ignoring resource cache configuration, using resourceCache attribute"); if (_relativeResourceBase!=null || _resourceBase!=null) throw new UnavailableException("resourceCache specified with resource bases"); - _cache=(ResourceCache)_servletContext.getAttribute(resourceCache); + _cache=(CachedContentFactory)_servletContext.getAttribute(resourceCache); } try { if (_cache==null && (max_cached_files!=-2 || max_cache_size!=-2 || max_cached_file_size!=-2)) { - _cache = new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_resourceService.isEtags(),_resourceService.getPrecompressedFormats()); + _cache = new CachedContentFactory(null,this,_mimeTypes,_useFileMappedBuffer,_resourceService.isEtags(),_resourceService.getPrecompressedFormats()); if (max_cache_size>=0) _cache.setMaxCacheSize(max_cache_size); if (max_cached_file_size>=-1) @@ -325,21 +332,31 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory LOG.debug("resource base = "+_resourceBase); } - private CompressedContentFormat[] parsePrecompressedFormats(String precompressed, boolean gzip) { + private CompressedContentFormat[] parsePrecompressedFormats(String precompressed, boolean gzip) + { List ret = new ArrayList<>(); - if (precompressed != null && precompressed.indexOf('=') > 0) { - for (String pair : precompressed.split(",")) { + if (precompressed != null && precompressed.indexOf('=') > 0) + { + for (String pair : precompressed.split(",")) + { String[] setting = pair.split("="); - String encoding = setting[0]; - String extension = setting[1]; - ret.add(new CompressedContentFormat(encoding, extension)); + String encoding = setting[0].trim(); + String extension = setting[1].trim(); + ret.add(new CompressedContentFormat(encoding,extension)); + if (gzip && !ret.contains(CompressedContentFormat.GZIP)) + ret.add(CompressedContentFormat.GZIP); } - } else if (precompressed != null) { - if (Boolean.parseBoolean(precompressed)) { + } + else if (precompressed != null) + { + if (Boolean.parseBoolean(precompressed)) + { ret.add(CompressedContentFormat.BR); ret.add(CompressedContentFormat.GZIP); } - } else if (gzip) { + } + else if (gzip) + { // gzip handling is for backwards compatibility with older Jetty ret.add(CompressedContentFormat.GZIP); } diff --git a/jetty-webapp/src/main/config/etc/webdefault.xml b/jetty-webapp/src/main/config/etc/webdefault.xml index d54d76f3072..5921269ec28 100644 --- a/jetty-webapp/src/main/config/etc/webdefault.xml +++ b/jetty-webapp/src/main/config/etc/webdefault.xml @@ -92,7 +92,14 @@ * * gzip If set to true, then static content will be served as * gzip content encoded if a matching resource is - * found ending with ".gz" + * found ending with ".gz" (default false) + * (deprecated: use precompressed) + * + * precompressed If set to a comma separated list of file extensions, these + * indicate compressed formats that are known to map to a mime-type + * that may be listed in a requests Accept-Encoding header. + * If set to a boolean True, then a default set of compressed formats + * will be used, otherwise no precompressed formats. * * resourceBase Set to replace the context resource base * @@ -164,10 +171,12 @@ maxCachedFiles 2048 + etags false