477278 Refactored DefaultServlet for cached Gzip & Etags

Refactored the DefaultServlet to better handle static gzipped files with etags in the cache.
Required a simplification of always having a HttpContent rather than the prior situation of
having either a Resource or a HttpContent.  So introduced a HttpContent.Factory, of which
the ResourceCache is the normal implementation, but there is also now a ResourceContentFactory
that creates content when there is no cache.

The Gzip resource is now associated with the normal resource, so less lookups are needed.
This also give scope for caching dynamic gzipping in the future.

The GzipHttpContent class has been introduced to send content with the headers of the
uncompress, but content of the compressed resource.
This commit is contained in:
Greg Wilkins 2015-09-17 18:15:13 +10:00
parent 03a601f2b2
commit 2d88fdf386
13 changed files with 745 additions and 250 deletions

View File

@ -0,0 +1,177 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.http;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import org.eclipse.jetty.http.MimeTypes.Type;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
public class GzipHttpContent implements HttpContent
{
private final HttpContent _content;
private final HttpContent _contentGz;
public final static String ETAG_GZIP="--gzip";
public final static PreEncodedHttpField CONTENT_ENCODING_GZIP=new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
public GzipHttpContent(HttpContent content, HttpContent contentGz)
{
_content=content;
_contentGz=contentGz;
}
@Override
public int hashCode()
{
return _content.hashCode();
}
@Override
public boolean equals(Object obj)
{
return _content.equals(obj);
}
@Override
public Resource getResource()
{
return _content.getResource();
}
@Override
public HttpField getETag()
{
return new HttpField(HttpHeader.ETAG,getETagValue());
}
@Override
public String getETagValue()
{
return _content.getResource().getWeakETag(ETAG_GZIP);
}
@Override
public HttpField getLastModified()
{
return _content.getLastModified();
}
@Override
public String getLastModifiedValue()
{
return _content.getLastModifiedValue();
}
@Override
public HttpField getContentType()
{
return _content.getContentType();
}
@Override
public String getContentTypeValue()
{
return _content.getContentTypeValue();
}
@Override
public HttpField getContentEncoding()
{
return CONTENT_ENCODING_GZIP;
}
@Override
public String getContentEncodingValue()
{
return CONTENT_ENCODING_GZIP.getValue();
}
@Override
public String getCharacterEncoding()
{
return _content.getCharacterEncoding();
}
@Override
public Type getMimeType()
{
return _content.getMimeType();
}
@Override
public void release()
{
_content.release();
}
@Override
public ByteBuffer getIndirectBuffer()
{
return _contentGz.getIndirectBuffer();
}
@Override
public ByteBuffer getDirectBuffer()
{
return _contentGz.getDirectBuffer();
}
@Override
public HttpField getContentLength()
{
return _contentGz.getContentLength();
}
@Override
public long getContentLengthValue()
{
return _contentGz.getContentLengthValue();
}
@Override
public InputStream getInputStream() throws IOException
{
return _contentGz.getInputStream();
}
@Override
public ReadableByteChannel getReadableByteChannel() throws IOException
{
return _contentGz.getReadableByteChannel();
}
@Override
public String toString()
{
return String.format("GzipHttpContent@%x{r=%s|%s,lm=%s|%s,ct=%s}",hashCode(),
_content.getResource(),_contentGz.getResource(),
_content.getResource().lastModified(),_contentGz.getResource().lastModified(),
getContentType());
}
@Override
public HttpContent getGzipContent()
{
return null;
}
}

View File

@ -44,6 +44,9 @@ public interface HttpContent
String getCharacterEncoding();
Type getMimeType();
HttpField getContentEncoding();
String getContentEncodingValue();
HttpField getContentLength();
long getContentLengthValue();
@ -60,4 +63,11 @@ public interface HttpContent
ReadableByteChannel getReadableByteChannel() throws IOException;
void release();
HttpContent getGzipContent();
public interface Factory
{
HttpContent getContent(String path) throws IOException;
}
}

View File

@ -39,33 +39,28 @@ public class ResourceHttpContent implements HttpContent
final Resource _resource;
final String _contentType;
final int _maxBuffer;
final String _etag;
HttpContent _gzip;
String _etag;
/* ------------------------------------------------------------ */
public ResourceHttpContent(final Resource resource, final String contentType)
{
this(resource,contentType,-1,false);
this(resource,contentType,-1,null);
}
/* ------------------------------------------------------------ */
public ResourceHttpContent(final Resource resource, final String contentType, int maxBuffer)
{
this(resource,contentType,maxBuffer,false);
this(resource,contentType,maxBuffer,null);
}
/* ------------------------------------------------------------ */
public ResourceHttpContent(final Resource resource, final String contentType, boolean etag)
{
this(resource,contentType,-1,etag);
}
/* ------------------------------------------------------------ */
public ResourceHttpContent(final Resource resource, final String contentType, int maxBuffer, boolean etag)
public ResourceHttpContent(final Resource resource, final String contentType, int maxBuffer, HttpContent gzip)
{
_resource=resource;
_contentType=contentType;
_maxBuffer=maxBuffer;
_etag=etag?resource.getWeakETag():null;
_gzip=gzip;
}
/* ------------------------------------------------------------ */
@ -82,6 +77,20 @@ public class ResourceHttpContent implements HttpContent
return _contentType==null?null:new HttpField(HttpHeader.CONTENT_TYPE,_contentType);
}
/* ------------------------------------------------------------ */
@Override
public HttpField getContentEncoding()
{
return null;
}
/* ------------------------------------------------------------ */
@Override
public String getContentEncodingValue()
{
return null;
}
/* ------------------------------------------------------------ */
@Override
public String getCharacterEncoding()
@ -132,14 +141,14 @@ public class ResourceHttpContent implements HttpContent
@Override
public HttpField getETag()
{
return _etag==null?null:new HttpField(HttpHeader.ETAG,_etag);
return new HttpField(HttpHeader.ETAG,getETagValue());
}
/* ------------------------------------------------------------ */
@Override
public String getETagValue()
{
return _etag;
return _resource.getWeakETag();
}
/* ------------------------------------------------------------ */
@ -205,6 +214,14 @@ public class ResourceHttpContent implements HttpContent
@Override
public String toString()
{
return String.format("%s@%x{r=%s}",this.getClass().getSimpleName(),hashCode(),_resource);
return String.format("%s@%x{r=%s,gz=%b}",this.getClass().getSimpleName(),hashCode(),_resource,_gzip!=null);
}
/* ------------------------------------------------------------ */
@Override
public HttpContent getGzipContent()
{
return _gzip==null?null:new GzipHttpContent(this,_gzip);
}
}

View File

@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.GzipHttpContent;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
@ -45,8 +46,8 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
public class ResourceCache
// TODO rename to ContentCache
public class ResourceCache implements HttpContent.Factory
{
private static final Logger LOG = Log.getLogger(ResourceCache.class);
@ -56,7 +57,8 @@ public class ResourceCache
private final ResourceFactory _factory;
private final ResourceCache _parent;
private final MimeTypes _mimeTypes;
private final boolean _etagSupported;
private final boolean _etags;
private final boolean _gzip;
private final boolean _useFileMappedBuffer;
private int _maxCachedFileSize =128*1024*1024;
@ -70,8 +72,9 @@ public class ResourceCache
* @param mimeTypes Mimetype to use for meta data
* @param useFileMappedBuffer true to file memory mapped buffers
* @param etags true to support etags
* @param gzip true to support gzip
*/
public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags)
public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags,boolean gzip)
{
_factory = factory;
_cache=new ConcurrentHashMap<String,CachedHttpContent>();
@ -80,7 +83,8 @@ public class ResourceCache
_mimeTypes=mimeTypes;
_parent=parent;
_useFileMappedBuffer=useFileMappedBuffer;
_etagSupported=etags;
_etags=etags;
_gzip=gzip;
}
/* ------------------------------------------------------------ */
@ -163,6 +167,14 @@ public class ResourceCache
}
}
/* ------------------------------------------------------------ */
@Deprecated
public HttpContent lookup(String pathInContext)
throws IOException
{
return getContent(pathInContext);
}
/* ------------------------------------------------------------ */
/** Get a Entry from the cache.
* Get either a valid entry object or create a new one if possible.
@ -174,7 +186,8 @@ public class ResourceCache
* the resource does not exist, then null is returned.
* @throws IOException Problem loading the resource
*/
public HttpContent lookup(String pathInContext)
@Override
public HttpContent getContent(String pathInContext)
throws IOException
{
// Is the content in this cache?
@ -206,6 +219,9 @@ public class ResourceCache
*/
protected boolean isCacheable(Resource resource)
{
if (_maxCachedFiles<=0)
return false;
long len = resource.length();
// Will it fit in the cache?
@ -216,16 +232,43 @@ public class ResourceCache
private HttpContent load(String pathInContext, Resource resource)
throws IOException
{
CachedHttpContent content=null;
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 (!resource.isDirectory() && isCacheable(resource))
if (isCacheable(resource))
{
// Create the Content (to increment the cache sizes before adding the content
content = new CachedHttpContent(pathInContext,resource);
CachedHttpContent content=null;
// Look for a gzip resource
if (_gzip)
{
String pathInContextGz=pathInContext+".gz";
CachedHttpContent contentGz = _cache.get(pathInContextGz);
if (contentGz==null || !contentGz.isValid())
{
contentGz=null;
Resource resourceGz=_factory.getResource(pathInContextGz);
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
{
contentGz = new CachedHttpContent(pathInContextGz,resourceGz,null);
shrinkCache();
CachedHttpContent added = _cache.putIfAbsent(pathInContextGz,contentGz);
if (added!=null)
{
contentGz.invalidate();
contentGz=added;
}
}
}
content = new CachedHttpContent(pathInContext,resource,contentGz);
}
else
content = new CachedHttpContent(pathInContext,resource,null);
// reduce the cache to an acceptable size.
shrinkCache();
@ -237,12 +280,28 @@ public class ResourceCache
content.invalidate();
content=added;
}
return content;
}
return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),getMaxCachedFileSize(),_etagSupported);
// Look for a gzip resource or content
String mt = _mimeTypes.getMimeByExtension(pathInContext);
if (_gzip)
{
// Is the gzip content cached?
String pathInContextGz=pathInContext+".gz";
CachedHttpContent contentGz = _cache.get(pathInContextGz);
if (contentGz!=null && contentGz.isValid() && contentGz.getResource().lastModified()>=resource.lastModified())
return new ResourceHttpContent(resource,mt,getMaxCachedFileSize(),contentGz);
// Is there a gzip resource?
Resource resourceGz=_factory.getResource(pathInContextGz);
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
return new ResourceHttpContent(resource,mt,getMaxCachedFileSize(),
new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),getMaxCachedFileSize()));
}
return new ResourceHttpContent(resource,mt,getMaxCachedFileSize());
}
/* ------------------------------------------------------------ */
@ -337,13 +396,14 @@ public class ResourceCache
final HttpField _lastModified;
final long _lastModifiedValue;
final HttpField _etag;
final CachedGzipHttpContent _gzipped;
volatile long _lastAccessed;
AtomicReference<ByteBuffer> _indirectBuffer=new AtomicReference<ByteBuffer>();
AtomicReference<ByteBuffer> _directBuffer=new AtomicReference<ByteBuffer>();
/* ------------------------------------------------------------ */
CachedHttpContent(String pathInContext,Resource resource)
CachedHttpContent(String pathInContext,Resource resource,CachedHttpContent gzipped)
{
_key=pathInContext;
_resource=resource;
@ -365,9 +425,11 @@ public class ResourceCache
_cachedFiles.incrementAndGet();
_lastAccessed=System.currentTimeMillis();
_etag=ResourceCache.this._etagSupported?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null;
_etag=ResourceCache.this._etags?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null;
_gzipped=gzipped==null?null:new CachedGzipHttpContent(this,gzipped);
}
/* ------------------------------------------------------------ */
public String getKey()
@ -428,7 +490,7 @@ public class ResourceCache
// Invalidate it
_cachedSize.addAndGet(-_contentLengthValue);
_cachedFiles.decrementAndGet();
_resource.close();
_resource.close();
}
/* ------------------------------------------------------------ */
@ -459,6 +521,20 @@ public class ResourceCache
{
return _contentType==null?null:_contentType.getValue();
}
/* ------------------------------------------------------------ */
@Override
public HttpField getContentEncoding()
{
return null;
}
/* ------------------------------------------------------------ */
@Override
public String getContentEncodingValue()
{
return null;
}
/* ------------------------------------------------------------ */
@Override
@ -479,7 +555,6 @@ public class ResourceCache
@Override
public void release()
{
// don't release while cached. Release when invalidated.
}
/* ------------------------------------------------------------ */
@ -557,12 +632,65 @@ public class ResourceCache
return _resource.getReadableByteChannel();
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s}",hashCode(),_resource,_resource.exists(),_lastModified,_contentType);
}
return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s,gz=%b}",hashCode(),_resource,_resource.exists(),_lastModified,_contentType,_gzipped!=null);
}
/* ------------------------------------------------------------ */
@Override
public HttpContent getGzipContent()
{
return (_gzipped!=null && _gzipped.isValid())?_gzipped:null;
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public class CachedGzipHttpContent extends GzipHttpContent
{
private final CachedHttpContent _content;
private final CachedHttpContent _contentGz;
private final HttpField _etag;
CachedGzipHttpContent(CachedHttpContent content, CachedHttpContent contentGz)
{
super(content,contentGz);
_content=content;
_contentGz=contentGz;
_etag=(ResourceCache.this._etags)?new PreEncodedHttpField(HttpHeader.ETAG,_content.getResource().getWeakETag("--gzip")):null;
}
public boolean isValid()
{
return _contentGz.isValid() && _content.isValid() && _content.getResource().lastModified() <= _contentGz.getResource().lastModified();
}
@Override
public HttpField getETag()
{
if (_etag!=null)
return _etag;
return super.getETag();
}
@Override
public String getETagValue()
{
if (_etag!=null)
return _etag.getValue();
return super.getETagValue();
}
@Override
public String toString()
{
return "Cached"+super.toString();
}
}
}

View File

@ -0,0 +1,104 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.io.IOException;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpContent.Factory;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.ResourceHttpContent;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
public class ResourceContentFactory implements Factory
{
private final ResourceFactory _factory;
private final MimeTypes _mimeTypes;
private final int _maxBufferSize;
private final boolean _gzip;
/* ------------------------------------------------------------ */
public ResourceContentFactory(ResourceFactory factory, MimeTypes mimeTypes, int maxBufferSize, boolean gzip)
{
_factory=factory;
_mimeTypes=mimeTypes;
_maxBufferSize=maxBufferSize;
_gzip=gzip;
}
/* ------------------------------------------------------------ */
/** Get a Entry from the cache.
* Get either a valid entry object or create a new one if possible.
*
* @param pathInContext The key into the cache
* @return The entry matching <code>pathInContext</code>, or a new entry
* if no matching entry was found. If the content exists but is not cachable,
* then a {@link ResourceHttpContent} instance is return. If
* the resource does not exist, then null is returned.
* @throws IOException Problem loading the resource
*/
@Override
public HttpContent getContent(String pathInContext)
throws IOException
{
// try loading the content from our factory.
Resource resource=_factory.getResource(pathInContext);
HttpContent loaded = load(pathInContext,resource);
return loaded;
}
/* ------------------------------------------------------------ */
private HttpContent load(String pathInContext, Resource resource)
throws IOException
{
if (resource==null || !resource.exists())
return null;
if (resource.isDirectory())
return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),_maxBufferSize);
// Look for a gzip resource or content
String mt = _mimeTypes.getMimeByExtension(pathInContext);
if (_gzip)
{
// Is there a gzip resource?
String pathInContextGz=pathInContext+".gz";
Resource resourceGz=_factory.getResource(pathInContextGz);
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
return new ResourceHttpContent(resource,mt,_maxBufferSize,
new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),_maxBufferSize));
}
return new ResourceHttpContent(resource,mt,_maxBufferSize);
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return "ResourceContentFactory["+_factory+"]@"+hashCode();
}
}

View File

@ -1311,7 +1311,6 @@ public class Response implements HttpServletResponse
public void putHeaders(HttpContent content,long contentLength, boolean etag)
{
HttpField lm = content.getLastModified();
if (lm!=null)
_fields.put(lm);
@ -1336,6 +1335,10 @@ public class Response implements HttpServletResponse
_mimeType=content.getMimeType();
}
HttpField ce=content.getContentEncoding();
if (ce!=null)
_fields.put(ce);
if (etag)
{
HttpField et = content.getETag();
@ -1362,7 +1365,11 @@ public class Response implements HttpServletResponse
String ct=content.getContentTypeValue();
if (ct!=null && response.getContentType()==null)
response.setContentType(content.getContentTypeValue());
response.setContentType(ct);
String ce=content.getContentEncodingValue();
if (ce!=null)
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),ce);
if (etag)
{

View File

@ -28,6 +28,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.GzipHttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
@ -61,10 +62,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
public final static String GZIP = "gzip";
public final static String DEFLATE = "deflate";
public final static String ETAG_GZIP="--gzip";
public final static String ETAG = "o.e.j.s.Gzip.ETag";
public final static int DEFAULT_MIN_GZIP_SIZE=16;
private int _minGzipSize=DEFAULT_MIN_GZIP_SIZE;
private int _compressionLevel=Deflater.DEFAULT_COMPRESSION;
private boolean _checkGzExists = true;
@ -79,6 +78,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
private HttpField _vary;
/* ------------------------------------------------------------ */
/**
* Instantiates a new gzip handler.
@ -418,8 +418,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
String etag = request.getHeader("If-None-Match");
if (etag!=null)
{
if (etag.contains(ETAG_GZIP))
request.setAttribute(ETAG,etag.replace(ETAG_GZIP,""));
if (etag.contains(GzipHttpContent.ETAG_GZIP))
request.setAttribute(ETAG,etag.replace(GzipHttpContent.ETAG_GZIP,""));
}
// install interceptor and handle

View File

@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import org.eclipse.jetty.http.GzipHttpContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@ -41,7 +42,6 @@ import org.eclipse.jetty.util.log.Logger;
public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
{
public static Logger LOG = Log.getLogger(GzipHttpOutputInterceptor.class);
private final static PreEncodedHttpField CONTENT_ENCODING_GZIP=new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
private final static byte[] GZIP_HEADER = new byte[] { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
public final static HttpField VARY_ACCEPT_ENCODING_USER_AGENT=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
@ -202,7 +202,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
return;
}
fields.put(CONTENT_ENCODING_GZIP);
fields.put(GzipHttpContent.CONTENT_ENCODING_GZIP);
_crc.reset();
_buffer=_channel.getByteBufferPool().acquire(_bufferSize,false);
BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
@ -213,7 +213,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
if (etag!=null)
{
int end = etag.length()-1;
etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHandler.ETAG_GZIP+'"':etag+GzipHandler.ETAG_GZIP;
etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP;
fields.put(HttpHeader.ETAG,etag);
}

View File

@ -49,9 +49,9 @@ public class ResourceCacheTest
Resource[] r = rc.getResources();
MimeTypes mime = new MimeTypes();
ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false);
ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false);
ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false);
ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false,false);
ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false,false);
ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false,false);
assertEquals("1 - one", getContent(rc1, "1.txt"));
assertEquals("2 - two", getContent(rc1, "2.txt"));
@ -79,8 +79,8 @@ public class ResourceCacheTest
Resource[] r = rc.getResources();
MimeTypes mime = new MimeTypes();
ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false);
ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false)
ResourceCache rc3 = new ResourceCache(null,r[2],mime,false,false,false);
ResourceCache rc2 = new ResourceCache(rc3,r[1],mime,false,false,false)
{
@Override
public boolean isCacheable(Resource resource)
@ -89,7 +89,7 @@ public class ResourceCacheTest
}
};
ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false);
ResourceCache rc1 = new ResourceCache(rc2,r[0],mime,false,false,false);
assertEquals("1 - one", getContent(rc1, "1.txt"));
assertEquals("2 - two", getContent(rc1, "2.txt"));
@ -130,7 +130,7 @@ public class ResourceCacheTest
directory=Resource.newResource(files[0].getParentFile().getAbsolutePath());
cache=new ResourceCache(null,directory,new MimeTypes(),false,false);
cache=new ResourceCache(null,directory,new MimeTypes(),false,false,false);
cache.setMaxCacheSize(95);
cache.setMaxCachedFileSize(85);
@ -243,7 +243,7 @@ public class ResourceCacheTest
Resource[] resources = rc.getResources();
MimeTypes mime = new MimeTypes();
ResourceCache cache = new ResourceCache(null,resources[0],mime,false,false);
ResourceCache cache = new ResourceCache(null,resources[0],mime,false,false,false);
assertEquals("4 - four", getContent(cache, "four.txt"));
assertEquals("4 - four (no extension)", getContent(cache, "four"));

View File

@ -54,6 +54,7 @@ import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.InclusiveByteRange;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.ResourceCache;
import org.eclipse.jetty.server.ResourceContentFactory;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.BufferUtil;
@ -161,6 +162,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
private Resource _resourceBase;
private ResourceCache _cache;
private HttpContent.Factory _contentFactory;
private MimeTypes _mimeTypes;
private String[] _welcomes;
@ -243,7 +245,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
String cc=getInitParameter("cacheControl");
if (cc!=null)
_cacheControl=new PreEncodedHttpField(HttpHeader.CACHE_CONTROL, cc);
String resourceCache = getInitParameter("resourceCache");
int max_cache_size=getInitInt("maxCacheSize", -2);
int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
@ -257,23 +259,23 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
_cache=(ResourceCache)_servletContext.getAttribute(resourceCache);
if (LOG.isDebugEnabled())
LOG.debug("Cache {}={}",resourceCache,_cache);
LOG.debug("Cache {}={}",resourceCache,_contentFactory);
}
_etags = getInitBoolean("etags",_etags);
try
{
if (_cache==null && (max_cached_files!=-2 || max_cache_size!=-2 || max_cached_file_size!=-2))
{
_cache= new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags);
_cache = new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags,_gzip);
if (max_cache_size>=0)
_cache.setMaxCacheSize(max_cache_size);
if (max_cached_file_size>=-1)
_cache.setMaxCachedFileSize(max_cached_file_size);
if (max_cached_files>=-1)
_cache.setMaxCachedFiles(max_cached_files);
_servletContext.setAttribute(resourceCache==null?"resourceCache":resourceCache,_cache);
}
}
catch (Exception e)
@ -281,33 +283,34 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
LOG.warn(Log.EXCEPTION,e);
throw new UnavailableException(e.toString());
}
_gzipEquivalentFileExtensions = new ArrayList<String>();
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
if (otherGzipExtensions != null)
{
//comma separated list
StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false);
while (tok.hasMoreTokens())
{
String s = tok.nextToken().trim();
_gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
}
}
else
{
//.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
_gzipEquivalentFileExtensions.add(".svgz");
}
_servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
for (ServletHolder h :_servletHandler.getServlets())
if (h.getServletInstance()==this)
_defaultHolder=h;
_contentFactory=_cache==null?new ResourceContentFactory(this,_mimeTypes,-1,_gzip):_cache; // TODO pass a buffer size
_gzipEquivalentFileExtensions = new ArrayList<String>();
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
if (otherGzipExtensions != null)
{
//comma separated list
StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false);
while (tok.hasMoreTokens())
{
String s = tok.nextToken().trim();
_gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
}
}
else
{
//.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
_gzipEquivalentFileExtensions.add(".svgz");
}
if (LOG.isDebugEnabled())
LOG.debug("resource base = "+_resourceBase);
_servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
for (ServletHolder h :_servletHandler.getServlets())
if (h.getServletInstance()==this)
_defaultHolder=h;
if (LOG.isDebugEnabled())
LOG.debug("resource base = "+_resourceBase);
}
/**
@ -422,8 +425,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
String servletPath=null;
String pathInfo=null;
Enumeration<String> reqRanges = null;
Boolean included =request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)!=null;
if (included!=null && included.booleanValue())
boolean included =request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)!=null;
if (included)
{
servletPath=(String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
pathInfo=(String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
@ -435,7 +438,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
else
{
included = Boolean.FALSE;
servletPath = _pathInfoOnly?"/":request.getServletPath();
pathInfo = request.getPathInfo();
@ -447,155 +449,72 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
// Find the resource and content
Resource resource=null;
boolean gzippable=_gzip && !endsWithSlash && !included && reqRanges==null;
HttpContent content=null;
boolean close_content=true;
boolean release_content=true;
try
{
// is gzip enabled?
String pathInContextGz=null;
boolean gzip=false;
if (!included.booleanValue() && _gzip && reqRanges==null && !endsWithSlash )
{
// Look for a gzip resource
pathInContextGz=pathInContext+".gz";
if (_cache==null)
resource=getResource(pathInContextGz);
else
{
content=_cache.lookup(pathInContextGz);
resource=(content==null)?null:content.getResource();
}
// Does a gzip resource exist?
if (resource!=null && resource.exists() && !resource.isDirectory())
{
// Tell caches that response may vary by accept-encoding
response.addHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
// Does the client accept gzip?
String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString());
if (accept!=null && accept.indexOf("gzip")>=0)
gzip=true;
}
}
// find resource
if (!gzip)
{
if (_cache==null)
resource=getResource(pathInContext);
else
{
content=_cache.lookup(pathInContext);
resource=content==null?null:content.getResource();
}
}
// Find the content
content=_contentFactory.getContent(pathInContext);
if (LOG.isDebugEnabled())
LOG.debug(String.format("uri=%s, resource=%s, content=%s",request.getRequestURI(),resource,content));
// Handle resource
if (resource==null || !resource.exists())
LOG.info("content={}",content);
// Not found?
if (content==null || !content.getResource().exists())
{
if (included)
throw new FileNotFoundException("!" + pathInContext);
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
else if (!resource.isDirectory())
// Directory?
if (content.getResource().isDirectory())
{
if (endsWithSlash && pathInContext.length()>1)
{
String q=request.getQueryString();
pathInContext=pathInContext.substring(0,pathInContext.length()-1);
if (q!=null&&q.length()!=0)
pathInContext+="?"+q;
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext)));
}
else
{
// ensure we have content
if (content==null)
content=new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(pathInContext),response.getBufferSize(),_etags);
if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
{
if (gzip || isGzippedContent(pathInContext))
{
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip");
String mt=_servletContext.getMimeType(pathInContext);
if (mt!=null)
response.setContentType(mt);
}
close_content=sendData(request,response,included.booleanValue(),resource,content,reqRanges);
}
}
sendWelcome(content,pathInContext,endsWithSlash,included,request,response);
return;
}
else
// Strip slash?
if (endsWithSlash && pathInContext.length()>1)
{
String welcome=null;
if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
{
StringBuffer buf=request.getRequestURL();
synchronized(buf)
{
int param=buf.lastIndexOf(";");
if (param<0)
buf.append('/');
else
buf.insert(param,'/');
String q=request.getQueryString();
if (q!=null&&q.length()!=0)
{
buf.append('?');
buf.append(q);
}
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(buf.toString()));
}
}
// else look for a welcome file
else if (null!=(welcome=getWelcomeFile(pathInContext)))
String q=request.getQueryString();
pathInContext=pathInContext.substring(0,pathInContext.length()-1);
if (q!=null&&q.length()!=0)
pathInContext+="?"+q;
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext)));
return;
}
// Conditional response?
if (!included && !passConditionalHeaders(request,response,content))
return;
// Gzip?
HttpContent gzip_content = gzippable?content.getGzipContent():null;
if (gzip_content!=null)
{
// Tell caches that response may vary by accept-encoding
response.addHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
// Does the client accept gzip?
String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString());
if (accept!=null && accept.indexOf("gzip")>=0)
{
if (LOG.isDebugEnabled())
LOG.debug("welcome={}",welcome);
if (_redirectWelcome)
{
// Redirect to the index
response.setContentLength(0);
String q=request.getQueryString();
if (q!=null&&q.length()!=0)
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q));
else
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)));
}
else
{
// Forward to the index
RequestDispatcher dispatcher=request.getRequestDispatcher(welcome);
if (dispatcher!=null)
{
if (included.booleanValue())
dispatcher.include(request,response);
else
{
request.setAttribute("org.eclipse.jetty.server.welcome",welcome);
dispatcher.forward(request,response);
}
}
}
}
else
{
content=new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(pathInContext),_etags);
if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
sendDirectory(request,response,resource,pathInContext);
LOG.debug("gzip={}",gzip_content);
content=gzip_content;
}
}
// TODO this should be done by HttpContent#getContentEncoding
if (isGzippedContent(pathInContext))
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip");
// Send the data
release_content=sendData(request,response,included,content,reqRanges);
}
catch(IllegalArgumentException e)
{
@ -605,17 +524,80 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
finally
{
if (close_content)
if (release_content)
{
if (content!=null)
content.release();
else if (resource!=null)
resource.close();
}
}
}
protected void sendWelcome(HttpContent content, String pathInContext, boolean endsWithSlash, boolean included, HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// Redirect to directory
if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
{
StringBuffer buf=request.getRequestURL();
synchronized(buf)
{
int param=buf.lastIndexOf(";");
if (param<0)
buf.append('/');
else
buf.insert(param,'/');
String q=request.getQueryString();
if (q!=null&&q.length()!=0)
{
buf.append('?');
buf.append(q);
}
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(buf.toString()));
}
return;
}
// look for a welcome file
String welcome=getWelcomeFile(pathInContext);
if (welcome!=null)
{
if (LOG.isDebugEnabled())
LOG.debug("welcome={}",welcome);
if (_redirectWelcome)
{
// Redirect to the index
response.setContentLength(0);
String q=request.getQueryString();
if (q!=null&&q.length()!=0)
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q));
else
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)));
}
else
{
// Forward to the index
RequestDispatcher dispatcher=request.getRequestDispatcher(welcome);
if (dispatcher!=null)
{
if (included)
dispatcher.include(request,response);
else
{
request.setAttribute("org.eclipse.jetty.server.welcome",welcome);
dispatcher.forward(request,response);
}
}
}
return;
}
if (included || passConditionalHeaders(request,response, content))
sendDirectory(request,response,content.getResource(),pathInContext);
}
/* ------------------------------------------------------------ */
protected boolean isGzippedContent(String path)
{
if (path == null) return false;
@ -699,7 +681,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
/* ------------------------------------------------------------ */
/* Check modification date headers.
*/
protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, Resource resource, HttpContent content)
protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, HttpContent content)
throws IOException
{
try
@ -820,7 +802,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
long ifmsl=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
if (ifmsl!=-1 && resource.lastModified()/1000 <= ifmsl/1000)
if (ifmsl!=-1 && content.getResource().lastModified()/1000 <= ifmsl/1000)
{
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
if (_etags)
@ -831,7 +813,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
// Parse the if[un]modified dates and compare to resource
if (ifums!=-1 && resource.lastModified()/1000 > ifums/1000)
if (ifums!=-1 && content.getResource().lastModified()/1000 > ifums/1000)
{
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
@ -894,12 +876,11 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
protected boolean sendData(HttpServletRequest request,
HttpServletResponse response,
boolean include,
Resource resource,
final HttpContent content,
Enumeration<String> reqRanges)
throws IOException
{
final long content_length = (content==null)?resource.length():content.getContentLengthValue();
final long content_length = content.getContentLengthValue();
// Get the output stream (or writer)
OutputStream out =null;
@ -908,7 +889,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{
out = response.getOutputStream();
// has a filter already written to the response?
// has something already written to the response?
written = out instanceof HttpOutput
? ((HttpOutput)out).isWritten()
: true;
@ -928,18 +909,18 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if (include)
{
// write without headers
resource.writeTo(out,0,content_length);
content.getResource().writeTo(out,0,content_length);
}
// else if we can't do a bypass write because of wrapping
else if (content==null || written || !(out instanceof HttpOutput))
else if (written || !(out instanceof HttpOutput))
{
// write normally
putHeaders(response,content,written?-1:0);
ByteBuffer buffer = (content==null)?null:content.getIndirectBuffer();
ByteBuffer buffer = content.getIndirectBuffer();
if (buffer!=null)
BufferUtil.writeTo(buffer,out);
else
resource.writeTo(out,0,content_length);
content.getResource().writeTo(out,0,content_length);
}
// else do a bypass write
else
@ -983,7 +964,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
// otherwise write content blocking
((HttpOutput)out).sendContent(content);
}
}
else
@ -998,7 +978,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
InclusiveByteRange.to416HeaderRangeString(content_length));
resource.writeTo(out,0,content_length);
content.getResource().writeTo(out,0,content_length);
return true;
}
@ -1014,7 +994,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
response.addDateHeader(HttpHeader.DATE.asString(),System.currentTimeMillis());
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
singleSatisfiableRange.toHeaderRangeString(content_length));
resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
content.getResource().writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
return true;
}
@ -1041,7 +1021,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
ctp = "multipart/byteranges; boundary=";
response.setContentType(ctp+multi.getBoundary());
InputStream in=resource.getInputStream();
InputStream in=content.getResource().getInputStream();
long pos=0;
// calculate the content-length
@ -1075,7 +1055,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if (start<pos)
{
in.close();
in=resource.getInputStream();
in=content.getResource().getInputStream();
pos=0;
}
if (pos<start)
@ -1089,7 +1069,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
else
// Handle cached resource
(resource).writeTo(multi,start,size);
content.getResource().writeTo(multi,start,size);
}
if (in!=null)
in.close();

View File

@ -99,7 +99,7 @@ public class DefaultServletTest
testdir.ensureEmpty();
/* create some content in the docroot */
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
assertTrue(resBase.mkdirs());
assertTrue(new File(resBase, "one").mkdir());
assertTrue(new File(resBase, "two").mkdir());
@ -131,7 +131,7 @@ public class DefaultServletTest
testdir.ensureEmpty();
/* create some content in the docroot */
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
assertTrue(new File(resBase, "one").mkdir());
assertTrue(new File(resBase, "two").mkdir());
@ -168,7 +168,7 @@ public class DefaultServletTest
testdir.ensureEmpty();
/* create some content in the docroot */
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
assertTrue(resBase.mkdirs());
File wackyDir = new File(resBase, "dir;"); // this should not be double-encoded.
assertTrue(wackyDir.mkdirs());
@ -220,7 +220,7 @@ public class DefaultServletTest
testdir.ensureEmpty();
/* create some content in the docroot */
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
assertTrue(resBase.mkdirs());
File index = new File(resBase, "index.html");
@ -320,7 +320,7 @@ public class DefaultServletTest
public void testWelcome() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File inde = new File(resBase, "index.htm");
File index = new File(resBase, "index.html");
@ -364,7 +364,7 @@ public class DefaultServletTest
public void testWelcomeServlet() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File inde = new File(resBase, "index.htm");
File index = new File(resBase, "index.html");
@ -412,7 +412,7 @@ public class DefaultServletTest
public void testResourceBase() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File foobar = new File(resBase, "foobar.txt");
File link = new File(resBase, "link.txt");
@ -448,7 +448,7 @@ public class DefaultServletTest
public void testWelcomeExactServlet() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File inde = new File(resBase, "index.htm");
File index = new File(resBase, "index.html");
@ -496,7 +496,7 @@ public class DefaultServletTest
public void testRangeRequests() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File data = new File(resBase, "data.txt");
createFile(data, "01234567890123456789012345678901234567890123456789012345678901234567890123456789");
@ -643,7 +643,7 @@ public class DefaultServletTest
public void testFiltered() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File file0 = new File(resBase, "data0.txt");
createFile(file0, "Hello Text 0");
@ -687,7 +687,7 @@ public class DefaultServletTest
public void testGzip() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File file0 = new File(resBase, "data0.txt");
createFile(file0, "Hello Text 0");
@ -701,6 +701,7 @@ public class DefaultServletTest
defholder.setInitParameter("redirectWelcome", "false");
defholder.setInitParameter("welcomeServlets", "false");
defholder.setInitParameter("gzip", "true");
defholder.setInitParameter("etags", "true");
defholder.setInitParameter("resourceBase", resBasePath);
String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
@ -708,7 +709,11 @@ public class DefaultServletTest
assertResponseContains("Content-Type: text/plain",response);
assertResponseContains("Hello Text 0",response);
assertResponseContains("Vary: Accept-Encoding",response);
assertResponseContains("ETag: ",response);
assertResponseNotContains("Content-Encoding: gzip",response);
int e=response.indexOf("ETag: ");
String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
assertResponseContains("Content-Length: 9", response);
@ -716,10 +721,70 @@ public class DefaultServletTest
assertResponseContains("Content-Type: text/plain",response);
assertResponseContains("Vary: Accept-Encoding",response);
assertResponseContains("Content-Encoding: gzip",response);
assertResponseContains("ETag: "+etag_gzip,response);
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
assertResponseContains("Content-Length: 9", response);
assertResponseContains("fake gzip",response);
assertResponseContains("Content-Type: application/gzip",response);
assertResponseNotContains("Vary: Accept-Encoding",response);
assertResponseNotContains("Content-Encoding: gzip",response);
assertResponseNotContains("ETag: "+etag_gzip,response);
assertResponseContains("ETag: ",response);
}
@Test
public void testCachedGzip() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File file0 = new File(resBase, "data0.txt");
createFile(file0, "Hello Text 0");
File file0gz = new File(resBase, "data0.txt.gz");
createFile(file0gz, "fake gzip");
String resBasePath = resBase.getAbsolutePath();
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
defholder.setInitParameter("dirAllowed", "false");
defholder.setInitParameter("redirectWelcome", "false");
defholder.setInitParameter("welcomeServlets", "false");
defholder.setInitParameter("gzip", "true");
defholder.setInitParameter("etags", "true");
defholder.setInitParameter("resourceBase", resBasePath);
defholder.setInitParameter("maxCachedFiles", "1024");
defholder.setInitParameter("maxCachedFileSize", "200000000");
defholder.setInitParameter("maxCacheSize", "256000000");
String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
assertResponseContains("Content-Length: 12", response);
assertResponseContains("Content-Type: text/plain",response);
assertResponseContains("Hello Text 0",response);
assertResponseContains("Vary: Accept-Encoding",response);
assertResponseContains("ETag: ",response);
assertResponseNotContains("Content-Encoding: gzip",response);
int e=response.indexOf("ETag: ");
String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
assertResponseContains("Content-Length: 9", response);
assertResponseContains("fake gzip",response);
assertResponseContains("Content-Type: text/plain",response);
assertResponseContains("Vary: Accept-Encoding",response);
assertResponseContains("Content-Encoding: gzip",response);
assertResponseContains("ETag: "+etag_gzip,response);
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
assertResponseContains("Content-Length: 9", response);
assertResponseContains("fake gzip",response);
assertResponseContains("Content-Type: application/gzip",response);
assertResponseNotContains("Vary: Accept-Encoding",response);
assertResponseNotContains("Content-Encoding: gzip",response);
assertResponseNotContains("ETag: "+etag_gzip,response);
assertResponseContains("ETag: ",response);
}
@Test
public void testIfModifiedSmall() throws Exception
@ -736,7 +801,7 @@ public class DefaultServletTest
public void testIfModified(String content) throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File file = new File(resBase, "file.txt");
@ -789,7 +854,7 @@ public class DefaultServletTest
public void testIfETag(String content) throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getFile("docroot");
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
File file = new File(resBase, "file.txt");

View File

@ -38,6 +38,7 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.GzipHttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
@ -147,7 +148,7 @@ public class GzipDefaultTest
//A HEAD request should have similar headers, but no body
response = tester.executeRequest("HEAD","/context/file.txt",5,TimeUnit.SECONDS);
assertThat("Response status",response.getStatus(),is(HttpStatus.OK_200));
assertThat("ETag", response.get("ETag"), containsString(GzipHandler.ETAG_GZIP));
assertThat("ETag", response.get("ETag"), containsString(GzipHttpContent.ETAG_GZIP));
assertThat("Content encoding", response.get("Content-Encoding"), containsString("gzip"));
assertNull("Content length", response.get("Content-Length"));

View File

@ -711,6 +711,11 @@ public abstract class Resource implements ResourceFactory, Closeable
* @return the weak ETag reference for this resource.
*/
public String getWeakETag()
{
return getWeakETag("");
}
public String getWeakETag(String suffix)
{
try
{
@ -725,6 +730,7 @@ public abstract class Resource implements ResourceFactory, Closeable
B64Code.encode(lastModified()^lhash,b);
B64Code.encode(length()^lhash,b);
b.append(suffix);
b.append('"');
return b.toString();
}