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:
parent
03a601f2b2
commit
2d88fdf386
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue