Improve HTTP header pre-encoding
The HttpContent class has been reworked to store HttpField instances, we may be generated on demand or instances of PreEncodedHttpField. The encoding of HTTP2 fields has been generalized to handle both indexed and literal fields, selected by header enum set. Default servlet and response classes have been cleaned up in how they set response headers.
This commit is contained in:
parent
61ec3efd65
commit
ec79a6f88e
|
@ -23,6 +23,7 @@ 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.BufferUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
||||
|
@ -33,13 +34,23 @@ import org.eclipse.jetty.util.resource.Resource;
|
|||
*/
|
||||
public interface HttpContent
|
||||
{
|
||||
String getContentType();
|
||||
String getLastModified();
|
||||
HttpField getContentType();
|
||||
String getContentTypeValue();
|
||||
String getCharacterEncoding();
|
||||
Type getMimeType();
|
||||
|
||||
HttpField getContentLength();
|
||||
long getContentLengthValue();
|
||||
|
||||
HttpField getLastModified();
|
||||
String getLastModifiedValue();
|
||||
|
||||
HttpField getETag();
|
||||
String getETagValue();
|
||||
|
||||
ByteBuffer getIndirectBuffer();
|
||||
ByteBuffer getDirectBuffer();
|
||||
String getETag();
|
||||
Resource getResource();
|
||||
long getContentLength();
|
||||
InputStream getInputStream() throws IOException;
|
||||
ReadableByteChannel getReadableByteChannel() throws IOException;
|
||||
void release();
|
||||
|
@ -50,49 +61,79 @@ public interface HttpContent
|
|||
public class ResourceAsHttpContent implements HttpContent
|
||||
{
|
||||
final Resource _resource;
|
||||
final String _mimeType;
|
||||
final String _contentType;
|
||||
final int _maxBuffer;
|
||||
final String _etag;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ResourceAsHttpContent(final Resource resource, final String mimeType)
|
||||
public ResourceAsHttpContent(final Resource resource, final String contentType)
|
||||
{
|
||||
this(resource,mimeType,-1,false);
|
||||
this(resource,contentType,-1,false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ResourceAsHttpContent(final Resource resource, final String mimeType, int maxBuffer)
|
||||
public ResourceAsHttpContent(final Resource resource, final String contentType, int maxBuffer)
|
||||
{
|
||||
this(resource,mimeType,maxBuffer,false);
|
||||
this(resource,contentType,maxBuffer,false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ResourceAsHttpContent(final Resource resource, final String mimeType, boolean etag)
|
||||
public ResourceAsHttpContent(final Resource resource, final String contentType, boolean etag)
|
||||
{
|
||||
this(resource,mimeType,-1,etag);
|
||||
this(resource,contentType,-1,etag);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ResourceAsHttpContent(final Resource resource, final String mimeType, int maxBuffer, boolean etag)
|
||||
public ResourceAsHttpContent(final Resource resource, final String contentType, int maxBuffer, boolean etag)
|
||||
{
|
||||
_resource=resource;
|
||||
_mimeType=mimeType;
|
||||
_contentType=contentType;
|
||||
_maxBuffer=maxBuffer;
|
||||
_etag=etag?resource.getWeakETag():null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getContentType()
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return _mimeType;
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public HttpField getContentType()
|
||||
{
|
||||
return _contentType==null?null:new HttpField(HttpHeader.CONTENT_TYPE,_contentType);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getLastModified()
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
return null;
|
||||
return _contentType==null?null:MimeTypes.getCharsetFromContentType(_contentType);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public Type getMimeType()
|
||||
{
|
||||
return _contentType==null?null:MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(_contentType));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public HttpField getLastModified()
|
||||
{
|
||||
long lm = _resource.lastModified();
|
||||
return lm>=0?new HttpField(HttpHeader.LAST_MODIFIED,DateGenerator.formatDate(lm)):null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
long lm = _resource.lastModified();
|
||||
return lm>=0?DateGenerator.formatDate(lm):null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -113,7 +154,14 @@ public interface HttpContent
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getETag()
|
||||
public HttpField getETag()
|
||||
{
|
||||
return _etag==null?null:new HttpField(HttpHeader.ETAG,_etag);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return _etag;
|
||||
}
|
||||
|
@ -136,7 +184,15 @@ public interface HttpContent
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public long getContentLength()
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
long l=_resource.length();
|
||||
return l==-1?null:new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,_resource.length());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public long getContentLengthValue()
|
||||
{
|
||||
return _resource.length();
|
||||
}
|
||||
|
@ -169,10 +225,12 @@ public interface HttpContent
|
|||
_resource.close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x{r=%s}",this.getClass().getSimpleName(),hashCode(),_resource);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -769,16 +769,29 @@ public class HttpGenerator
|
|||
break;
|
||||
|
||||
case CONTENT_LENGTH:
|
||||
{
|
||||
long content_length = _info.getContentLength();
|
||||
if ((response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
{
|
||||
// known length but not actually set.
|
||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
||||
BufferUtil.putDecLong(header, content_length);
|
||||
header.put(HttpTokens.CRLF);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SELF_DEFINING_CONTENT:
|
||||
{
|
||||
// TODO - Should we do this? Why was it not required before?
|
||||
long content_length = _info.getContentLength();
|
||||
if (content_length>0)
|
||||
{
|
||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
||||
BufferUtil.putDecLong(header, content_length);
|
||||
header.put(HttpTokens.CRLF);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NO_CONTENT:
|
||||
if (response!=null && status >= 200 && status != 204 && status != 304)
|
||||
header.put(CONTENT_LENGTH_0);
|
||||
|
|
|
@ -177,8 +177,6 @@ public class HpackContext
|
|||
__headerEntryTable[h.ordinal()]=entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int _maxHeaderTableSizeInBytes;
|
||||
private int _headerTableSizeInBytes;
|
||||
|
@ -302,6 +300,16 @@ public class HpackContext
|
|||
return _headerTable.index(entry)+__staticTable.length-1;
|
||||
}
|
||||
|
||||
public static int staticIndex(HttpHeader header)
|
||||
{
|
||||
if (header==null)
|
||||
return 0;
|
||||
Entry entry=__staticNameMap.get(header.asString());
|
||||
if (entry==null)
|
||||
return 0;
|
||||
return entry.getSlot();
|
||||
}
|
||||
|
||||
private void evict()
|
||||
{
|
||||
while (_headerTableSizeInBytes>_maxHeaderTableSizeInBytes)
|
||||
|
|
|
@ -44,14 +44,14 @@ public class HpackEncoder
|
|||
private final static HttpField[] __status= new HttpField[599];
|
||||
|
||||
|
||||
private final static EnumSet<HttpHeader> __DO_NOT_HUFFMAN =
|
||||
final static EnumSet<HttpHeader> __DO_NOT_HUFFMAN =
|
||||
EnumSet.of(
|
||||
HttpHeader.AUTHORIZATION,
|
||||
HttpHeader.CONTENT_MD5,
|
||||
HttpHeader.PROXY_AUTHENTICATE,
|
||||
HttpHeader.PROXY_AUTHORIZATION);
|
||||
|
||||
private final static EnumSet<HttpHeader> __DO_NOT_INDEX =
|
||||
final static EnumSet<HttpHeader> __DO_NOT_INDEX =
|
||||
EnumSet.of(
|
||||
// HttpHeader.C_PATH, // TODO more data needed
|
||||
// HttpHeader.DATE, // TODO more data needed
|
||||
|
@ -67,13 +67,13 @@ public class HpackEncoder
|
|||
HttpHeader.LOCATION,
|
||||
HttpHeader.RANGE,
|
||||
HttpHeader.RETRY_AFTER,
|
||||
HttpHeader.EXPIRES,
|
||||
// HttpHeader.EXPIRES,
|
||||
HttpHeader.LAST_MODIFIED,
|
||||
HttpHeader.SET_COOKIE,
|
||||
HttpHeader.SET_COOKIE2);
|
||||
|
||||
|
||||
private final static EnumSet<HttpHeader> __NEVER_INDEX =
|
||||
final static EnumSet<HttpHeader> __NEVER_INDEX =
|
||||
EnumSet.of(
|
||||
HttpHeader.AUTHORIZATION,
|
||||
HttpHeader.SET_COOKIE,
|
||||
|
@ -82,7 +82,7 @@ public class HpackEncoder
|
|||
static
|
||||
{
|
||||
for (HttpStatus.Code code : HttpStatus.Code.values())
|
||||
__status[code.getCode()]=new HttpField(":status",Integer.toString(code.getCode()));
|
||||
__status[code.getCode()]=new PreEncodedHttpField(HttpHeader.C_STATUS,Integer.toString(code.getCode()));
|
||||
}
|
||||
|
||||
private final HpackContext _context;
|
||||
|
@ -185,7 +185,7 @@ public class HpackEncoder
|
|||
_context.resize(maxHeaderTableSize);
|
||||
}
|
||||
|
||||
private void encode(ByteBuffer buffer, HttpField field)
|
||||
public void encode(ByteBuffer buffer, HttpField field)
|
||||
{
|
||||
final int p=_debug?buffer.position():-1;
|
||||
|
||||
|
@ -216,7 +216,6 @@ public class HpackEncoder
|
|||
// Unknown field entry, so we will have to send literally.
|
||||
final boolean indexed;
|
||||
|
||||
|
||||
// But do we know it's name?
|
||||
HttpHeader header = field.getHeader();
|
||||
|
||||
|
@ -225,9 +224,18 @@ public class HpackEncoder
|
|||
{
|
||||
// Select encoding strategy for unknown header names
|
||||
Entry name = _context.get(field.getName());
|
||||
|
||||
|
||||
if (field instanceof PreEncodedHttpField)
|
||||
{
|
||||
int i=buffer.position();
|
||||
((PreEncodedHttpField)field).putTo(buffer,HttpVersion.HTTP_2);
|
||||
byte b=buffer.get(i);
|
||||
indexed=b<0||b>=0x40;
|
||||
if (_debug)
|
||||
encoding=indexed?"PreEncodedIdx":"PreEncoded";
|
||||
}
|
||||
// has the custom header name been seen before?
|
||||
if (name==null)
|
||||
else if (name==null)
|
||||
{
|
||||
// unknown name and value, so let's index this just in case it is
|
||||
// the first time we have seen a custom name or a custom field.
|
||||
|
@ -237,7 +245,6 @@ public class HpackEncoder
|
|||
encodeValue(buffer,true,field.getValue());
|
||||
if (_debug)
|
||||
encoding="LitHuffNHuffVIdx";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -255,7 +262,17 @@ public class HpackEncoder
|
|||
// Select encoding strategy for known header names
|
||||
Entry name = _context.get(header);
|
||||
|
||||
if (__DO_NOT_INDEX.contains(header))
|
||||
if (field instanceof PreEncodedHttpField)
|
||||
{
|
||||
// Preencoded field
|
||||
int i=buffer.position();
|
||||
((PreEncodedHttpField)field).putTo(buffer,HttpVersion.HTTP_2);
|
||||
byte b=buffer.get(i);
|
||||
indexed=b<0||b>=0x40;
|
||||
if (_debug)
|
||||
encoding=indexed?"PreEncodedIdx":"PreEncoded";
|
||||
}
|
||||
else if (__DO_NOT_INDEX.contains(header))
|
||||
{
|
||||
// Non indexed field
|
||||
indexed=false;
|
||||
|
@ -272,22 +289,13 @@ public class HpackEncoder
|
|||
}
|
||||
else if (header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>1)
|
||||
{
|
||||
// Non indexed content length for non zero value
|
||||
// Non indexed content length for 2 digits or more
|
||||
indexed=false;
|
||||
encodeName(buffer,(byte)0x00,4,header.asString(),name);
|
||||
encodeValue(buffer,true,field.getValue());
|
||||
if (_debug)
|
||||
encoding="LitIdxNS"+(1+NBitInteger.octectsNeeded(4,_context.index(name)))+"HuffV!Idx";
|
||||
}
|
||||
else if (field instanceof PreEncodedHttpField)
|
||||
{
|
||||
// Preencoded field
|
||||
indexed=true;
|
||||
((PreEncodedHttpField)field).putTo(buffer,HttpVersion.HTTP_2);
|
||||
if (_debug)
|
||||
encoding=((name==null)?"LitHuffN":("LitIdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(6,_context.index(name)))))+
|
||||
"HuffVIdx";
|
||||
}
|
||||
else
|
||||
{
|
||||
// indexed
|
||||
|
@ -332,7 +340,7 @@ public class HpackEncoder
|
|||
}
|
||||
}
|
||||
|
||||
private void encodeValue(ByteBuffer buffer, boolean huffman, String value)
|
||||
static void encodeValue(ByteBuffer buffer, boolean huffman, String value)
|
||||
{
|
||||
if (huffman)
|
||||
{
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.nio.ByteBuffer;
|
|||
import org.eclipse.jetty.http.HttpFieldPreEncoder;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
||||
|
@ -50,24 +49,48 @@ public class HpackFieldPreEncoder implements HttpFieldPreEncoder
|
|||
@Override
|
||||
public byte[] getEncodedField(HttpHeader header, String name, String value)
|
||||
{
|
||||
boolean not_indexed=HpackEncoder.__DO_NOT_INDEX.contains(header);
|
||||
|
||||
ByteBuffer buffer = BufferUtil.allocate(name.length()+value.length()+10);
|
||||
BufferUtil.clearToFill(buffer);
|
||||
buffer.put((byte)0x40);
|
||||
Entry entry = header==null?null:HpackContext.getStatic(header);
|
||||
if (entry==null)
|
||||
boolean huffman;
|
||||
int bits;
|
||||
|
||||
if (not_indexed)
|
||||
{
|
||||
// Non indexed field
|
||||
boolean never_index=HpackEncoder.__NEVER_INDEX.contains(header);
|
||||
huffman=!HpackEncoder.__DO_NOT_HUFFMAN.contains(header);
|
||||
buffer.put(never_index?(byte)0x10:(byte)0x00);
|
||||
bits=4;
|
||||
}
|
||||
else if (header==HttpHeader.CONTENT_LENGTH && value.length()>1)
|
||||
{
|
||||
// Non indexed content length for 2 digits or more
|
||||
buffer.put((byte)0x00);
|
||||
huffman=true;
|
||||
bits=4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// indexed
|
||||
buffer.put((byte)0x40);
|
||||
huffman=!HpackEncoder.__DO_NOT_HUFFMAN.contains(header);
|
||||
bits=6;
|
||||
}
|
||||
|
||||
int name_idx=HpackContext.staticIndex(header);
|
||||
if (name_idx>0)
|
||||
NBitInteger.encode(buffer,bits,name_idx);
|
||||
else
|
||||
{
|
||||
buffer.put((byte)0x80);
|
||||
NBitInteger.encode(buffer,7,Huffman.octetsNeededLC(name));
|
||||
Huffman.encodeLC(buffer,name);
|
||||
}
|
||||
else
|
||||
{
|
||||
NBitInteger.encode(buffer,6,entry.getSlot());
|
||||
}
|
||||
|
||||
buffer.put((byte)0x80);
|
||||
NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value));
|
||||
Huffman.encode(buffer,value);
|
||||
HpackEncoder.encodeValue(buffer,huffman,value);
|
||||
|
||||
BufferUtil.flipToFlush(buffer,0);
|
||||
return BufferUtil.toArray(buffer);
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class Http2Server
|
|||
context.addFilter(PushCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST))
|
||||
.setInitParameter("ports","443,6443,8443");
|
||||
context.addServlet(new ServletHolder(servlet), "/test/*");
|
||||
context.addServlet(DefaultServlet.class, "/");
|
||||
context.addServlet(DefaultServlet.class, "/").setInitParameter("maxCacheSize","81920");
|
||||
server.setHandler(context);
|
||||
|
||||
|
||||
|
@ -122,6 +122,10 @@ public class Http2Server
|
|||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String code=request.getParameter("code");
|
||||
if (code!=null)
|
||||
response.setStatus(Integer.parseInt(code));
|
||||
|
||||
HttpSession session = request.getSession(true);
|
||||
if (session.isNew())
|
||||
response.addCookie(new Cookie("bigcookie",
|
||||
|
|
|
@ -33,7 +33,11 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
|
||||
import org.eclipse.jetty.http.DateGenerator;
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.http.MimeTypes.Type;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -258,7 +262,7 @@ public class ResourceCache
|
|||
if (c1._lastAccessed>c2._lastAccessed)
|
||||
return 1;
|
||||
|
||||
if (c1._length<c2._length)
|
||||
if (c1._contentLengthValue<c2._contentLengthValue)
|
||||
return -1;
|
||||
|
||||
return c1._key.compareTo(c2._key);
|
||||
|
@ -322,13 +326,16 @@ public class ResourceCache
|
|||
*/
|
||||
public class Content implements HttpContent
|
||||
{
|
||||
final Resource _resource;
|
||||
final int _length;
|
||||
final String _key;
|
||||
final long _lastModified;
|
||||
final ByteBuffer _lastModifiedBytes;
|
||||
final ByteBuffer _contentType;
|
||||
final String _etag;
|
||||
final Resource _resource;
|
||||
final int _contentLengthValue;
|
||||
final HttpField _contentType;
|
||||
final String _characterEncoding;
|
||||
final MimeTypes.Type _mimeType;
|
||||
final HttpField _contentLength;
|
||||
final HttpField _lastModified;
|
||||
final long _lastModifiedValue;
|
||||
final HttpField _etag;
|
||||
|
||||
volatile long _lastAccessed;
|
||||
AtomicReference<ByteBuffer> _indirectBuffer=new AtomicReference<ByteBuffer>();
|
||||
|
@ -340,18 +347,24 @@ public class ResourceCache
|
|||
_key=pathInContext;
|
||||
_resource=resource;
|
||||
|
||||
String mimeType = _mimeTypes.getMimeByExtension(_resource.toString());
|
||||
_contentType=(mimeType==null?null:BufferUtil.toBuffer(mimeType));
|
||||
boolean exists=resource.exists();
|
||||
_lastModified=exists?resource.lastModified():-1;
|
||||
_lastModifiedBytes=_lastModified<0?null:BufferUtil.toBuffer(DateGenerator.formatDate(_lastModified));
|
||||
String contentType = _mimeTypes.getMimeByExtension(_resource.toString());
|
||||
_contentType=contentType==null?null:new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,contentType);
|
||||
_characterEncoding = _contentType==null?null:MimeTypes.getCharsetFromContentType(contentType);
|
||||
_mimeType = _contentType==null?null:MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(contentType));
|
||||
|
||||
_length=exists?(int)resource.length():0;
|
||||
_cachedSize.addAndGet(_length);
|
||||
boolean exists=resource.exists();
|
||||
_lastModifiedValue=exists?resource.lastModified():-1L;
|
||||
_lastModified=_lastModifiedValue==-1?null
|
||||
:new PreEncodedHttpField(HttpHeader.LAST_MODIFIED,DateGenerator.formatDate(_lastModifiedValue));
|
||||
|
||||
_contentLengthValue=exists?(int)resource.length():0;
|
||||
_contentLength=new PreEncodedHttpField(HttpHeader.CONTENT_LENGTH,Long.toString(_contentLengthValue));
|
||||
|
||||
_cachedSize.addAndGet(_contentLengthValue);
|
||||
_cachedFiles.incrementAndGet();
|
||||
_lastAccessed=System.currentTimeMillis();
|
||||
|
||||
_etag=ResourceCache.this._etagSupported?resource.getWeakETag():null;
|
||||
_etag=ResourceCache.this._etagSupported?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -382,15 +395,22 @@ public class ResourceCache
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getETag()
|
||||
public HttpField getETag()
|
||||
{
|
||||
return _etag;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return _etag.getValue();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
boolean isValid()
|
||||
{
|
||||
if (_lastModified==_resource.lastModified() && _length==_resource.length())
|
||||
if (_lastModifiedValue==_resource.lastModified() && _contentLengthValue==_resource.length())
|
||||
{
|
||||
_lastAccessed=System.currentTimeMillis();
|
||||
return true;
|
||||
|
@ -405,25 +425,55 @@ public class ResourceCache
|
|||
protected void invalidate()
|
||||
{
|
||||
// Invalidate it
|
||||
_cachedSize.addAndGet(-_length);
|
||||
_cachedSize.addAndGet(-_contentLengthValue);
|
||||
_cachedFiles.decrementAndGet();
|
||||
_resource.close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getLastModified()
|
||||
public HttpField getLastModified()
|
||||
{
|
||||
return BufferUtil.toString(_lastModifiedBytes);
|
||||
return _lastModified;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
return _lastModified==null?null:_lastModified.getValue();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public HttpField getContentType()
|
||||
{
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return _contentType==null?null:_contentType.getValue();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
return _characterEncoding;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String getContentType()
|
||||
public Type getMimeType()
|
||||
{
|
||||
return BufferUtil.toString(_contentType);
|
||||
return _mimeType;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void release()
|
||||
|
@ -473,12 +523,19 @@ public class ResourceCache
|
|||
return null;
|
||||
return buffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public long getContentLength()
|
||||
public long getContentLengthValue()
|
||||
{
|
||||
return _length;
|
||||
return _contentLengthValue;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -504,7 +561,7 @@ public class ResourceCache
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s}",hashCode(),_resource,_resource.exists(),BufferUtil.toString(_lastModifiedBytes),_contentType);
|
||||
return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s}",hashCode(),_resource,_resource.exists(),_lastModified,_contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Enumeration;
|
|||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
@ -48,6 +49,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
||||
|
@ -66,6 +68,7 @@ public class Response implements HttpServletResponse
|
|||
private static final String __COOKIE_DELIM="\",;\\ \t";
|
||||
private final static String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
|
||||
private final static int __MIN_BUFFER_SIZE = 1;
|
||||
private final static HttpField __EXPIRES_01JAN1970 = new PreEncodedHttpField(HttpHeader.EXPIRES,DateGenerator.__01Jan1970);
|
||||
|
||||
|
||||
// Cookie building buffer. Reduce garbage for cookie using applications
|
||||
|
@ -146,31 +149,6 @@ public class Response implements HttpServletResponse
|
|||
_fields.clear();
|
||||
_explicitEncoding=false;
|
||||
}
|
||||
|
||||
public void setHeaders(HttpContent httpContent)
|
||||
{
|
||||
Response response = _channel.getResponse();
|
||||
String contentType = httpContent.getContentType();
|
||||
if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
|
||||
setContentType(contentType);
|
||||
|
||||
if (httpContent.getContentLength() > 0)
|
||||
setLongContentLength(httpContent.getContentLength());
|
||||
|
||||
String lm = httpContent.getLastModified();
|
||||
if (lm != null)
|
||||
response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm);
|
||||
else if (httpContent.getResource() != null)
|
||||
{
|
||||
long lml = httpContent.getResource().lastModified();
|
||||
if (lml != -1)
|
||||
response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
|
||||
}
|
||||
|
||||
String etag=httpContent.getETag();
|
||||
if (etag!=null)
|
||||
response.getHttpFields().put(HttpHeader.ETAG,etag);
|
||||
}
|
||||
|
||||
public HttpOutput getHttpOutput()
|
||||
{
|
||||
|
@ -376,7 +354,7 @@ public class Response implements HttpServletResponse
|
|||
_fields.add(HttpHeader.SET_COOKIE.toString(), buf.toString());
|
||||
|
||||
// Expire responses with set-cookie headers so they do not get cached.
|
||||
_fields.put(HttpHeader.EXPIRES.toString(), DateGenerator.__01Jan1970);
|
||||
_fields.put(__EXPIRES_01JAN1970);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1110,7 +1088,7 @@ public class Response implements HttpServletResponse
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setContentType(String contentType)
|
||||
{
|
||||
|
@ -1366,4 +1344,67 @@ public class Response implements HttpServletResponse
|
|||
out=_httpWriter;
|
||||
}
|
||||
}
|
||||
|
||||
public void putHeaders(HttpContent content,long contentLength, boolean etag)
|
||||
{
|
||||
|
||||
HttpField lm = content.getLastModified();
|
||||
if (lm!=null)
|
||||
_fields.put(lm);
|
||||
|
||||
if (contentLength==0)
|
||||
{
|
||||
_fields.put(content.getContentLength());
|
||||
_contentLength=content.getContentLengthValue();
|
||||
}
|
||||
else if (contentLength>0)
|
||||
{
|
||||
_fields.putLongField(HttpHeader.CONTENT_LENGTH,contentLength);
|
||||
_contentLength=contentLength;
|
||||
}
|
||||
|
||||
HttpField ct=content.getContentType();
|
||||
if (ct!=null)
|
||||
{
|
||||
_fields.put(ct);
|
||||
_contentType=ct.getValue();
|
||||
_characterEncoding=content.getCharacterEncoding();
|
||||
_mimeType=content.getMimeType();
|
||||
}
|
||||
|
||||
if (etag)
|
||||
{
|
||||
HttpField et = content.getETag();
|
||||
if (et!=null)
|
||||
_fields.put(et);
|
||||
}
|
||||
}
|
||||
|
||||
public static void putHeaders(HttpServletResponse response, HttpContent content, long contentLength, boolean etag)
|
||||
{
|
||||
long lml=content.getResource().lastModified();
|
||||
if (lml>=0)
|
||||
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml);
|
||||
|
||||
if (contentLength==0)
|
||||
contentLength=content.getContentLengthValue();
|
||||
if (contentLength >=0)
|
||||
{
|
||||
if (contentLength<Integer.MAX_VALUE)
|
||||
response.setContentLength((int)contentLength);
|
||||
else
|
||||
response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(contentLength));
|
||||
}
|
||||
|
||||
String ct=content.getContentTypeValue();
|
||||
if (ct!=null && response.getContentType()==null)
|
||||
response.setContentType(content.getContentTypeValue());
|
||||
|
||||
if (etag)
|
||||
{
|
||||
String et=content.getETagValue();
|
||||
if (et!=null)
|
||||
response.setHeader(HttpHeader.ETAG.asString(),et);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ public class ResourceCacheTest
|
|||
HttpContent content;
|
||||
content=cache.lookup(names[8]);
|
||||
assertTrue(content!=null);
|
||||
assertEquals(80,content.getContentLength());
|
||||
assertEquals(80,content.getContentLengthValue());
|
||||
|
||||
assertEquals(80,cache.getCachedSize());
|
||||
assertEquals(1,cache.getCachedFiles());
|
||||
|
|
|
@ -39,6 +39,7 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.DateParser;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -50,6 +51,7 @@ import org.eclipse.jetty.http.PathMap.MappedEntry;
|
|||
import org.eclipse.jetty.io.WriterOutputStream;
|
||||
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.Response;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
|
@ -703,21 +705,57 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
{
|
||||
try
|
||||
{
|
||||
String ifm=null;
|
||||
String ifnm=null;
|
||||
String ifms=null;
|
||||
long ifums=-1;
|
||||
|
||||
if (request instanceof Request)
|
||||
{
|
||||
HttpFields fields = ((Request)request).getHttpFields();
|
||||
for (int i=fields.size();i-->0;)
|
||||
{
|
||||
HttpField field=fields.getField(i);
|
||||
switch (field.getHeader())
|
||||
{
|
||||
case IF_MATCH:
|
||||
ifm=field.getValue();
|
||||
break;
|
||||
case IF_NONE_MATCH:
|
||||
ifnm=field.getValue();
|
||||
break;
|
||||
case IF_MODIFIED_SINCE:
|
||||
ifms=field.getValue();
|
||||
break;
|
||||
case IF_UNMODIFIED_SINCE:
|
||||
ifums=DateParser.parseDate(field.getValue());
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ifm=request.getHeader(HttpHeader.IF_MATCH.asString());
|
||||
ifnm=request.getHeader(HttpHeader.IF_NONE_MATCH.asString());
|
||||
ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
|
||||
ifums=request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString());
|
||||
}
|
||||
|
||||
if (!HttpMethod.HEAD.is(request.getMethod()))
|
||||
{
|
||||
if (_etags)
|
||||
{
|
||||
String ifm=request.getHeader(HttpHeader.IF_MATCH.asString());
|
||||
if (ifm!=null)
|
||||
{
|
||||
boolean match=false;
|
||||
if (content.getETag()!=null)
|
||||
if (content.getETagValue()!=null)
|
||||
{
|
||||
QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifm,", ",false,true);
|
||||
while (!match && quoted.hasMoreTokens())
|
||||
{
|
||||
String tag = quoted.nextToken();
|
||||
if (content.getETag().equals(tag))
|
||||
if (content.getETagValue().equals(tag))
|
||||
match=true;
|
||||
}
|
||||
}
|
||||
|
@ -729,34 +767,33 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
}
|
||||
|
||||
String if_non_match_etag=request.getHeader(HttpHeader.IF_NONE_MATCH.asString());
|
||||
if (if_non_match_etag!=null && content.getETag()!=null)
|
||||
if (ifnm!=null && content.getETagValue()!=null)
|
||||
{
|
||||
// Look for GzipFiltered version of etag
|
||||
if (content.getETag().equals(request.getAttribute("o.e.j.s.GzipFilter.ETag")))
|
||||
if (content.getETagValue().equals(request.getAttribute("o.e.j.s.GzipFilter.ETag")))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
response.setHeader(HttpHeader.ETAG.asString(),if_non_match_etag);
|
||||
response.setHeader(HttpHeader.ETAG.asString(),ifnm);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle special case of exact match.
|
||||
if (content.getETag().equals(if_non_match_etag))
|
||||
if (content.getETagValue().equals(ifnm))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle list of tags
|
||||
QuotedStringTokenizer quoted = new QuotedStringTokenizer(if_non_match_etag,", ",false,true);
|
||||
QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifnm,", ",false,true);
|
||||
while (quoted.hasMoreTokens())
|
||||
{
|
||||
String tag = quoted.nextToken();
|
||||
if (content.getETag().equals(tag))
|
||||
if (content.getETagValue().equals(tag))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -767,16 +804,15 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
|
||||
// Handle if modified since
|
||||
String ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
|
||||
if (ifms!=null)
|
||||
{
|
||||
//Get jetty's Response impl
|
||||
String mdlm=content.getLastModified();
|
||||
String mdlm=content.getLastModifiedValue();
|
||||
if (mdlm!=null && ifms.equals(mdlm))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
if (_etags)
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue());
|
||||
response.flushBuffer();
|
||||
return false;
|
||||
}
|
||||
|
@ -786,15 +822,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
if (_etags)
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETagValue());
|
||||
response.flushBuffer();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the if[un]modified dates and compare to resource
|
||||
long date=request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString());
|
||||
if (date!=-1 && resource.lastModified()/1000 > date/1000)
|
||||
if (ifums!=-1 && resource.lastModified()/1000 > ifums/1000)
|
||||
{
|
||||
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
|
||||
return false;
|
||||
|
@ -862,7 +897,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
Enumeration<String> reqRanges)
|
||||
throws IOException
|
||||
{
|
||||
final long content_length = (content==null)?resource.length():content.getContentLength();
|
||||
final long content_length = (content==null)?resource.length():content.getContentLengthValue();
|
||||
|
||||
// Get the output stream (or writer)
|
||||
OutputStream out =null;
|
||||
|
@ -890,13 +925,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
// if there were no ranges, send entire entity
|
||||
if (include)
|
||||
{
|
||||
// write without headers
|
||||
resource.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))
|
||||
{
|
||||
// write normally
|
||||
writeHeaders(response,content,written?-1:content_length);
|
||||
putHeaders(response,content,written?-1:0);
|
||||
ByteBuffer buffer = (content==null)?null:content.getIndirectBuffer();
|
||||
if (buffer!=null)
|
||||
BufferUtil.writeTo(buffer,out);
|
||||
|
@ -907,14 +943,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
else
|
||||
{
|
||||
// write the headers
|
||||
if (response instanceof Response)
|
||||
{
|
||||
Response r = (Response)response;
|
||||
writeOptionHeaders(r.getHttpFields());
|
||||
r.setHeaders(content);
|
||||
}
|
||||
else
|
||||
writeHeaders(response,content,content_length);
|
||||
putHeaders(response,content,0);
|
||||
|
||||
// write the content asynchronously if supported
|
||||
if (request.isAsyncSupported())
|
||||
|
@ -961,7 +990,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
// if there are no satisfiable ranges, send 416 response
|
||||
if (ranges==null || ranges.size()==0)
|
||||
{
|
||||
writeHeaders(response, content, content_length);
|
||||
putHeaders(response,content,0);
|
||||
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
|
||||
InclusiveByteRange.to416HeaderRangeString(content_length));
|
||||
|
@ -975,7 +1004,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
{
|
||||
InclusiveByteRange singleSatisfiableRange = ranges.get(0);
|
||||
long singleLength = singleSatisfiableRange.getSize(content_length);
|
||||
writeHeaders(response,content,singleLength );
|
||||
putHeaders(response,content,singleLength);
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
if (!response.containsHeader(HttpHeader.DATE.asString()))
|
||||
response.addDateHeader(HttpHeader.DATE.asString(),System.currentTimeMillis());
|
||||
|
@ -989,8 +1018,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
// 216 response which does not require an overall
|
||||
// content-length header
|
||||
//
|
||||
writeHeaders(response,content,-1);
|
||||
String mimetype=(content==null?null:content.getContentType());
|
||||
putHeaders(response,content,-1);
|
||||
String mimetype=(content==null?null:content.getContentTypeValue());
|
||||
if (mimetype==null)
|
||||
LOG.warn("Unknown mimetype for "+request.getRequestURI());
|
||||
MultiPartOutputStream multi = new MultiPartOutputStream(out);
|
||||
|
@ -1066,82 +1095,30 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
|
||||
protected void putHeaders(HttpServletResponse response,HttpContent content, long contentLength)
|
||||
{
|
||||
if (content == null)
|
||||
{
|
||||
// No content, then no headers to process
|
||||
// This is possible during bypass write because of wrapping
|
||||
// See .sendData() for more details.
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.getContentType()!=null && response.getContentType()==null)
|
||||
response.setContentType(content.getContentType().toString());
|
||||
|
||||
if (response instanceof Response)
|
||||
{
|
||||
Response r=(Response)response;
|
||||
HttpFields fields = r.getHttpFields();
|
||||
Response r = (Response)response;
|
||||
r.putHeaders(content,contentLength,_etags);
|
||||
HttpFields f = r.getHttpFields();
|
||||
if (_acceptRanges)
|
||||
f.put(ACCEPT_RANGES);
|
||||
|
||||
if (content.getLastModified()!=null)
|
||||
fields.put(HttpHeader.LAST_MODIFIED,content.getLastModified());
|
||||
else if (content.getResource()!=null)
|
||||
{
|
||||
long lml=content.getResource().lastModified();
|
||||
if (lml!=-1)
|
||||
fields.putDateField(HttpHeader.LAST_MODIFIED,lml);
|
||||
}
|
||||
|
||||
if (count != -1)
|
||||
r.setLongContentLength(count);
|
||||
|
||||
writeOptionHeaders(fields);
|
||||
|
||||
if (_etags)
|
||||
fields.put(HttpHeader.ETAG,content.getETag());
|
||||
if (_cacheControl!=null)
|
||||
f.put(_cacheControl);
|
||||
}
|
||||
else
|
||||
{
|
||||
long lml=content.getResource().lastModified();
|
||||
if (lml>=0)
|
||||
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml);
|
||||
Response.putHeaders(response,content,contentLength,_etags);
|
||||
if (_acceptRanges)
|
||||
response.setHeader(ACCEPT_RANGES.getName(),ACCEPT_RANGES.getValue());
|
||||
|
||||
if (count != -1)
|
||||
{
|
||||
if (count<Integer.MAX_VALUE)
|
||||
response.setContentLength((int)count);
|
||||
else
|
||||
response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(count));
|
||||
}
|
||||
|
||||
writeOptionHeaders(response);
|
||||
|
||||
if (_etags)
|
||||
response.setHeader(HttpHeader.ETAG.asString(),content.getETag());
|
||||
if (_cacheControl!=null)
|
||||
response.setHeader(_cacheControl.getName(),_cacheControl.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void writeOptionHeaders(HttpFields fields)
|
||||
{
|
||||
if (_acceptRanges)
|
||||
fields.put(ACCEPT_RANGES);
|
||||
|
||||
if (_cacheControl!=null)
|
||||
fields.put(_cacheControl);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void writeOptionHeaders(HttpServletResponse response)
|
||||
{
|
||||
if (_acceptRanges)
|
||||
response.setHeader(HttpHeader.ACCEPT_RANGES.asString(),"bytes");
|
||||
|
||||
if (_cacheControl!=null)
|
||||
response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl.getValue());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see javax.servlet.Servlet#destroy()
|
||||
|
|
|
@ -662,16 +662,21 @@ public class DefaultServletTest
|
|||
assertResponseContains("Content-Length: 12", response);
|
||||
assertResponseNotContains("Extra Info", response);
|
||||
|
||||
server.stop();
|
||||
context.addFilter(OutputFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
server.start();
|
||||
|
||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
|
||||
assertResponseContains("Content-Length: 2", response); // 20 something long
|
||||
assertResponseContains("Extra Info", response);
|
||||
assertResponseNotContains("Content-Length: 12", response);
|
||||
|
||||
server.stop();
|
||||
context.getServletHandler().setFilterMappings(new FilterMapping[]{});
|
||||
context.getServletHandler().setFilters(new FilterHolder[]{});
|
||||
|
||||
context.addFilter(WriterFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
server.start();
|
||||
|
||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
|
||||
assertResponseContains("Content-Length: 2", response); // 20 something long
|
||||
assertResponseContains("Extra Info", response);
|
||||
|
|
|
@ -53,7 +53,7 @@ public class GzipFilterDefaultTest
|
|||
{
|
||||
return Arrays.asList(new Object[][]
|
||||
{
|
||||
{ AsyncGzipFilter.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, AsyncGzipFilter.GZIP },
|
||||
{ GzipFilter.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, GzipFilter.DEFLATE },
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue