cleanup and javadoc HttpContent (#12069)
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
parent
fb37792671
commit
a9a0f164ca
|
@ -31,7 +31,6 @@ import org.eclipse.jetty.http.PreEncodedHttpField;
|
|||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.io.IOResources;
|
||||
import org.eclipse.jetty.io.Retainable;
|
||||
import org.eclipse.jetty.io.RetainableByteBuffer;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -187,8 +186,11 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
CachingHttpContent removed = _cache.remove(content.getKey());
|
||||
if (removed != null)
|
||||
{
|
||||
// After the removed entry is released, the caching buffer has been re-pooled which
|
||||
// makes the length invalid, so getContentLengthValue() must be called before release().
|
||||
long contentLengthValue = removed.getContentLengthValue();
|
||||
removed.release();
|
||||
_cachedSize.addAndGet(-removed.getBytesOccupied());
|
||||
_cachedSize.addAndGet(-contentLengthValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +220,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return false;
|
||||
|
||||
// Will it fit in the cache?
|
||||
long len = httpContent.getBytesOccupied();
|
||||
long len = httpContent.getContentLengthValue();
|
||||
return (len <= _maxCachedFileSize && len <= _maxCacheSize);
|
||||
}
|
||||
|
||||
|
@ -230,11 +232,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
{
|
||||
cachingHttpContent.setLastAccessedNanos(NanoTime.now());
|
||||
if (cachingHttpContent.isValid())
|
||||
{
|
||||
// If retain fails the CachingHttpContent was already evicted.
|
||||
if (cachingHttpContent.retain())
|
||||
return (cachingHttpContent instanceof NotFoundHttpContent) ? null : cachingHttpContent;
|
||||
}
|
||||
return (cachingHttpContent instanceof NotFoundHttpContent) ? null : cachingHttpContent;
|
||||
else
|
||||
removeFromCache(cachingHttpContent);
|
||||
}
|
||||
|
@ -247,27 +245,33 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
AtomicBoolean added = new AtomicBoolean();
|
||||
cachingHttpContent = _cache.computeIfAbsent(path, key ->
|
||||
{
|
||||
CachingHttpContent cachingContent = (httpContent == null) ? newNotFoundContent(key) : newCachedContent(key, httpContent);
|
||||
added.set(true);
|
||||
_cachedSize.addAndGet(cachingContent.getBytesOccupied());
|
||||
return cachingContent;
|
||||
try
|
||||
{
|
||||
CachingHttpContent cachingContent = (httpContent == null) ? newNotFoundContent(key) : newCachedContent(key, httpContent);
|
||||
long contentLengthValue = cachingContent.getContentLengthValue();
|
||||
if (contentLengthValue < 0L)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Content at path '{}' with unknown length is not cacheable: {}", path, httpContent);
|
||||
return null;
|
||||
}
|
||||
added.set(true);
|
||||
_cachedSize.addAndGet(contentLengthValue);
|
||||
return cachingContent;
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Content at path '{}' is not cacheable: {}", path, httpContent, x);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// If retain fails the CachingHttpContent was already evicted.
|
||||
if (!cachingHttpContent.retain())
|
||||
return httpContent;
|
||||
|
||||
if (added.get())
|
||||
{
|
||||
// We want to shrink cache only if we have just added an entry.
|
||||
shrinkCache();
|
||||
}
|
||||
else if (httpContent != null)
|
||||
{
|
||||
// If we did not add an entry we are using a cached version added by someone else,
|
||||
// so we should release the local content taken from the authority.
|
||||
httpContent.release();
|
||||
}
|
||||
|
||||
return (cachingHttpContent instanceof NotFoundHttpContent) ? null : cachingHttpContent;
|
||||
}
|
||||
|
@ -292,7 +296,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
|
||||
boolean isValid();
|
||||
|
||||
boolean retain();
|
||||
void release();
|
||||
}
|
||||
|
||||
protected class CachedHttpContent extends HttpContent.Wrapper implements CachingHttpContent
|
||||
|
@ -300,18 +304,13 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
private final RetainableByteBuffer _buffer;
|
||||
private final String _cacheKey;
|
||||
private final HttpField _etagField;
|
||||
private final long _contentLengthValue;
|
||||
private volatile long _lastAccessed;
|
||||
private final Set<CompressedContentFormat> _compressedFormats;
|
||||
private final String _lastModifiedValue;
|
||||
private final String _characterEncoding;
|
||||
private final MimeTypes.Type _mimeType;
|
||||
private final HttpField _contentLength;
|
||||
private final Instant _lastModifiedInstant;
|
||||
private final HttpField _lastModified;
|
||||
private final long _bytesOccupied;
|
||||
private final boolean _isValid;
|
||||
private final Retainable.ReferenceCounter _referenceCount = new Retainable.ReferenceCounter();
|
||||
|
||||
public CachedHttpContent(String key, HttpContent httpContent)
|
||||
{
|
||||
|
@ -326,50 +325,25 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
etagField = new PreEncodedHttpField(HttpHeader.ETAG, eTagValue);
|
||||
}
|
||||
_etagField = etagField;
|
||||
_contentLengthValue = httpContent.getContentLengthValue();
|
||||
boolean isValid = true;
|
||||
_contentLength = httpContent.getContentLength();
|
||||
long contentLengthValue = httpContent.getContentLengthValue();
|
||||
|
||||
// Read the content into memory if the HttpContent does not already have a buffer.
|
||||
RetainableByteBuffer buffer = null;
|
||||
try
|
||||
{
|
||||
if (_contentLengthValue <= _maxCachedFileSize)
|
||||
buffer = IOResources.toRetainableByteBuffer(httpContent.getResource(), _bufferPool, _useDirectByteBuffers);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
isValid = false;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.warn("Failed to read Resource: {}", httpContent.getResource(), t);
|
||||
else
|
||||
LOG.warn("Failed to read Resource: {} - {}", httpContent.getResource(), t.toString());
|
||||
}
|
||||
if (contentLengthValue < 0)
|
||||
throw new IllegalArgumentException("Resource length is unknown");
|
||||
if (contentLengthValue > _maxCachedFileSize)
|
||||
throw new IllegalArgumentException("Resource is too large: length " + contentLengthValue + " > " + _maxCachedFileSize);
|
||||
|
||||
// Read the content into memory
|
||||
_buffer = IOResources.toRetainableByteBuffer(httpContent.getResource(), _bufferPool, _useDirectByteBuffers);
|
||||
|
||||
_buffer = buffer;
|
||||
_isValid = isValid;
|
||||
_bytesOccupied = httpContent.getBytesOccupied();
|
||||
_lastModifiedValue = httpContent.getLastModifiedValue();
|
||||
_characterEncoding = httpContent.getCharacterEncoding();
|
||||
_compressedFormats = httpContent.getPreCompressedContentFormats();
|
||||
_mimeType = httpContent.getMimeType();
|
||||
_contentLength = httpContent.getContentLength();
|
||||
_lastModifiedInstant = httpContent.getLastModifiedInstant();
|
||||
_lastModified = httpContent.getLastModified();
|
||||
_lastAccessed = NanoTime.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthValue()
|
||||
{
|
||||
return _contentLengthValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBytesOccupied()
|
||||
{
|
||||
return _bytesOccupied;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastAccessedNanos()
|
||||
{
|
||||
|
@ -393,30 +367,21 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
{
|
||||
try
|
||||
{
|
||||
sink.write(true, BufferUtil.slice(_buffer.getByteBuffer(), (int)offset, (int)length), callback);
|
||||
_buffer.retain();
|
||||
sink.write(true, BufferUtil.slice(_buffer.getByteBuffer(), (int)offset, (int)length), Callback.from(_buffer::release, callback));
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
// BufferUtil.slice() may fail if offset and/or length are out of bounds.
|
||||
_buffer.release();
|
||||
callback.failed(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retain()
|
||||
{
|
||||
return _referenceCount.tryRetain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release()
|
||||
{
|
||||
if (_referenceCount.release())
|
||||
{
|
||||
if (_buffer != null)
|
||||
_buffer.release();
|
||||
super.release();
|
||||
}
|
||||
_buffer.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -431,12 +396,6 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return _etagField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return _etagField == null ? null : _etagField.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
|
@ -455,6 +414,12 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return _contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthValue()
|
||||
{
|
||||
return _buffer.remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastModifiedInstant()
|
||||
{
|
||||
|
@ -467,16 +432,10 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return _lastModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
return _lastModifiedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return _isValid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,12 +475,6 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
|
@ -540,12 +493,6 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentEncodingValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
|
@ -570,24 +517,12 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getETag()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource()
|
||||
{
|
||||
|
@ -616,11 +551,5 @@ public class CachingHttpContentFactory implements HttpContent.Factory
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retain()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,11 @@ package org.eclipse.jetty.http.content;
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -87,6 +90,9 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
|
|||
private static class SingleBufferFileMappedHttpContent extends HttpContent.Wrapper
|
||||
{
|
||||
private final ByteBuffer _buffer;
|
||||
private final HttpField _contentLength;
|
||||
private final HttpField _lastModified;
|
||||
private final Instant _lastModifiedInstant;
|
||||
|
||||
private SingleBufferFileMappedHttpContent(HttpContent content) throws IOException
|
||||
{
|
||||
|
@ -95,6 +101,9 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
|
|||
if (path == null)
|
||||
throw new IOException("Cannot memory map Content whose Resource is not backed by a Path: " + content.getResource());
|
||||
_buffer = BufferUtil.toMappedBuffer(path);
|
||||
_contentLength = new HttpField(HttpHeader.CONTENT_LENGTH, Integer.toString(_buffer.remaining()));
|
||||
_lastModified = content.getLastModified();
|
||||
_lastModifiedInstant = content.getLastModifiedInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,17 +120,38 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getBytesOccupied()
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthValue()
|
||||
{
|
||||
return _buffer.remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastModifiedInstant()
|
||||
{
|
||||
return _lastModifiedInstant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getLastModified()
|
||||
{
|
||||
return _lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MultiBufferFileMappedHttpContent extends HttpContent.Wrapper
|
||||
{
|
||||
private final ByteBuffer[] _buffers;
|
||||
private final int maxBufferSize;
|
||||
private final long _bytesOccupied;
|
||||
private final HttpField _contentLength;
|
||||
private final long _contentLengthValue;
|
||||
private final HttpField _lastModified;
|
||||
private final Instant _lastModifiedInstant;
|
||||
|
||||
private MultiBufferFileMappedHttpContent(HttpContent content, int maxBufferSize) throws IOException
|
||||
{
|
||||
|
@ -143,7 +173,10 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
|
|||
currentPos += len;
|
||||
total += _buffers[i].remaining();
|
||||
}
|
||||
_bytesOccupied = total;
|
||||
_contentLengthValue = total;
|
||||
_contentLength = new HttpField(HttpHeader.CONTENT_LENGTH, Long.toString(total));
|
||||
_lastModified = content.getLastModified();
|
||||
_lastModifiedInstant = content.getLastModifiedInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -151,9 +184,9 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
|
|||
{
|
||||
try
|
||||
{
|
||||
if (offset > getBytesOccupied())
|
||||
if (offset > getContentLengthValue())
|
||||
throw new IllegalArgumentException("Offset outside of mapped file range");
|
||||
if (length > -1 && length + offset > getBytesOccupied())
|
||||
if (length > -1 && length + offset > getContentLengthValue())
|
||||
throw new IllegalArgumentException("Offset / length outside of mapped file range");
|
||||
|
||||
int beginIndex = Math.toIntExact(offset / maxBufferSize);
|
||||
|
@ -205,9 +238,27 @@ public class FileMappingHttpContentFactory implements HttpContent.Factory
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getBytesOccupied()
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
return _bytesOccupied;
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthValue()
|
||||
{
|
||||
return _contentLengthValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastModifiedInstant()
|
||||
{
|
||||
return _lastModifiedInstant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getLastModified()
|
||||
{
|
||||
return _lastModified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,77 +19,144 @@ import java.util.Set;
|
|||
|
||||
import org.eclipse.jetty.http.CompressedContentFormat;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.MimeTypes.Type;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
||||
/**
|
||||
* HttpContent interface.
|
||||
* <p>This information represents all the information about a
|
||||
* static resource that is needed to evaluate conditional headers
|
||||
* and to serve the content if need be. It can be implemented
|
||||
* either transiently (values and fields generated on demand) or
|
||||
* persistently (values and fields pre-generated in anticipation of
|
||||
* reuse in from a cache).
|
||||
* <p>The {@code HttpContent} interface represents all the information about a
|
||||
* static {@link Resource} that is needed to evaluate conditional headers
|
||||
* and to eventually serve the actual content.
|
||||
* It can be implemented either transiently (values and fields generated on
|
||||
* demand) or persistently (values and fields pre-generated in anticipation
|
||||
* of reuse in from a cache).
|
||||
* </p>
|
||||
*/
|
||||
public interface HttpContent
|
||||
{
|
||||
/**
|
||||
* Get the {@link HttpHeader#CONTENT_TYPE} of this HTTP content.
|
||||
*
|
||||
* @return the content type field, or null if the type of this content is not known.
|
||||
*/
|
||||
HttpField getContentType();
|
||||
|
||||
String getContentTypeValue();
|
||||
|
||||
String getCharacterEncoding();
|
||||
|
||||
Type getMimeType();
|
||||
|
||||
/**
|
||||
* Get the {@link HttpHeader#CONTENT_ENCODING} of this HTTP content.
|
||||
*
|
||||
* @return the content encoding field, or null if the encoding of this content is not known.
|
||||
*/
|
||||
HttpField getContentEncoding();
|
||||
|
||||
String getContentEncodingValue();
|
||||
|
||||
/**
|
||||
* Get the {@link HttpHeader#CONTENT_LENGTH} of this HTTP content. The value of the returned field
|
||||
* must always match the value returned by {@link #getContentLengthValue()}.
|
||||
*
|
||||
* @return the content length field, or null if the length of this content is not known.
|
||||
*/
|
||||
HttpField getContentLength();
|
||||
|
||||
long getContentLengthValue();
|
||||
|
||||
Instant getLastModifiedInstant();
|
||||
|
||||
/**
|
||||
* Get the {@link HttpHeader#LAST_MODIFIED} of this HTTP content. The value of the returned field
|
||||
* must always match the value returned by {@link #getLastModifiedInstant()}.
|
||||
*
|
||||
* @return the last modified field, or null if the last modification time of this content is not known.
|
||||
*/
|
||||
HttpField getLastModified();
|
||||
|
||||
String getLastModifiedValue();
|
||||
|
||||
/**
|
||||
* Get the {@link HttpHeader#ETAG} of this HTTP content.
|
||||
*
|
||||
* @return the ETag, or null if this content has no ETag.
|
||||
*/
|
||||
HttpField getETag();
|
||||
|
||||
String getETagValue();
|
||||
/**
|
||||
* Get the character encoding of this HTTP content.
|
||||
*
|
||||
* @return the character encoding, or null if the character encoding of this content is not known.
|
||||
*/
|
||||
String getCharacterEncoding();
|
||||
|
||||
/**
|
||||
* Get the Mime type of this HTTP content.
|
||||
*
|
||||
* @return the mime type, or null if the mime type of this content is not known.
|
||||
*/
|
||||
Type getMimeType();
|
||||
|
||||
/**
|
||||
* Get the last modified instant of this resource.
|
||||
*
|
||||
* @return the last modified instant, or null if that instant of this content is not known.
|
||||
* @see #getLastModified()
|
||||
*/
|
||||
Instant getLastModifiedInstant();
|
||||
|
||||
/**
|
||||
* Get the content length of this resource.
|
||||
*
|
||||
* @return the content length of this resource, or -1 if it is not known.
|
||||
* @see #getContentLength()
|
||||
*/
|
||||
long getContentLengthValue();
|
||||
|
||||
/**
|
||||
* Get the {@link Resource} backing this HTTP content.
|
||||
*
|
||||
* @return the backing resource.
|
||||
*/
|
||||
Resource getResource();
|
||||
|
||||
/**
|
||||
* <p>Write a subset of this HTTP content, to a {@link Content.Sink}.</p>
|
||||
* Asynchronously write a subset of this HTTP content to a {@link Content.Sink}.
|
||||
* Calling this method does not consume the content, so it can be used repeatedly.
|
||||
*
|
||||
* @param sink the sink to write to.
|
||||
* @param offset the offset byte of the resource to start from.
|
||||
* @param length the length of the resource's contents to copy, -1 for the full length.
|
||||
* @param callback the callback to notify when writing is done.
|
||||
*/
|
||||
void writeTo(Content.Sink sink, long offset, long length, Callback callback);
|
||||
|
||||
default long getBytesOccupied()
|
||||
{
|
||||
return getContentLengthValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available pre-compressed formats for this content.
|
||||
*
|
||||
* @return Set of available pre-compressed formats for this content, or null if this has not been checked.
|
||||
*/
|
||||
Set<CompressedContentFormat> getPreCompressedContentFormats();
|
||||
|
||||
void release();
|
||||
// TODO get rid of these?
|
||||
default String getContentTypeValue()
|
||||
{
|
||||
HttpField contentType = getContentType();
|
||||
return contentType == null ? null : contentType.getValue();
|
||||
}
|
||||
|
||||
default String getContentEncodingValue()
|
||||
{
|
||||
HttpField contentEncoding = getContentEncoding();
|
||||
return contentEncoding == null ? null : contentEncoding.getValue();
|
||||
}
|
||||
|
||||
default String getETagValue()
|
||||
{
|
||||
HttpField eTag = getETag();
|
||||
return eTag == null ? null : eTag.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory of {@link HttpContent}.
|
||||
*/
|
||||
interface Factory
|
||||
{
|
||||
/**
|
||||
* @param path The path within the context to the resource
|
||||
* @return A {@link HttpContent}
|
||||
* Get the {@link HttpContent} instance of a path.
|
||||
*
|
||||
* @param path The path.
|
||||
* @return A {@link HttpContent} instance.
|
||||
* @throws IOException if unable to get content
|
||||
*/
|
||||
HttpContent getContent(String path) throws IOException;
|
||||
|
@ -120,12 +187,6 @@ public interface HttpContent
|
|||
return _delegate.getContentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return _delegate.getContentTypeValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
|
@ -144,12 +205,6 @@ public interface HttpContent
|
|||
return _delegate.getContentEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentEncodingValue()
|
||||
{
|
||||
return _delegate.getContentEncodingValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
|
@ -174,24 +229,12 @@ public interface HttpContent
|
|||
return _delegate.getLastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
return _delegate.getLastModifiedValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getETag()
|
||||
{
|
||||
return _delegate.getETag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return _delegate.getETagValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource()
|
||||
{
|
||||
|
@ -204,24 +247,12 @@ public interface HttpContent
|
|||
_delegate.writeTo(sink, offset, length, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBytesOccupied()
|
||||
{
|
||||
return _delegate.getBytesOccupied();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompressedContentFormat> getPreCompressedContentFormats()
|
||||
{
|
||||
return _delegate.getPreCompressedContentFormats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release()
|
||||
{
|
||||
_delegate.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -59,12 +59,6 @@ public class PreCompressedHttpContent implements HttpContent
|
|||
return _etag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return getETag().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastModifiedInstant()
|
||||
{
|
||||
|
@ -77,36 +71,18 @@ public class PreCompressedHttpContent implements HttpContent
|
|||
return _precompressedContent.getLastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
return _precompressedContent.getLastModifiedValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentType()
|
||||
{
|
||||
return _content.getContentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return _content.getContentTypeValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentEncoding()
|
||||
{
|
||||
return _format.getContentEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentEncodingValue()
|
||||
{
|
||||
return _format.getContentEncoding().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
|
@ -153,10 +129,4 @@ public class PreCompressedHttpContent implements HttpContent
|
|||
{
|
||||
return _content.getPreCompressedContentFormats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release()
|
||||
{
|
||||
_precompressedContent.release();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.http.content;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -39,30 +38,22 @@ import org.eclipse.jetty.util.resource.Resource;
|
|||
public class ResourceHttpContent implements HttpContent
|
||||
{
|
||||
final Resource _resource;
|
||||
final Path _path;
|
||||
final String _contentType;
|
||||
final HttpField _contentType;
|
||||
final HttpField _etag;
|
||||
final ByteBufferPool.Sized _sizedBufferPool;
|
||||
|
||||
public ResourceHttpContent(Resource resource, String contentType, ByteBufferPool.Sized sizedByteBufferPool)
|
||||
{
|
||||
_resource = resource;
|
||||
_path = resource.getPath();
|
||||
_contentType = contentType;
|
||||
_contentType = contentType == null ? null : new HttpField(HttpHeader.CONTENT_TYPE, contentType);
|
||||
_etag = EtagUtils.createWeakEtagField(resource);
|
||||
_sizedBufferPool = sizedByteBufferPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentType()
|
||||
{
|
||||
return _contentType == null ? null : new HttpField(HttpHeader.CONTENT_TYPE, _contentType);
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,22 +62,16 @@ public class ResourceHttpContent implements HttpContent
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentEncodingValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding()
|
||||
{
|
||||
return _contentType == null ? null : MimeTypes.getCharsetFromContentType(_contentType);
|
||||
return _contentType == null ? null : MimeTypes.getCharsetFromContentType(_contentType.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getMimeType()
|
||||
{
|
||||
return _contentType == null ? null : MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(_contentType));
|
||||
return _contentType == null ? null : MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(_contentType.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,32 +87,17 @@ public class ResourceHttpContent implements HttpContent
|
|||
return new HttpField(HttpHeader.LAST_MODIFIED, DateGenerator.formatDate(lm));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastModifiedValue()
|
||||
{
|
||||
Instant lm = _resource.lastModified();
|
||||
return DateGenerator.formatDate(lm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getETag()
|
||||
{
|
||||
return _etag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
if (_etag == null)
|
||||
return null;
|
||||
return _etag.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentLength()
|
||||
{
|
||||
long l = getContentLengthValue();
|
||||
return l == -1 ? null : new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH, l);
|
||||
return l == -1L ? null : new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,9 +129,4 @@ public class ResourceHttpContent implements HttpContent
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
import org.eclipse.jetty.http.ByteRange;
|
||||
import org.eclipse.jetty.http.CompressedContentFormat;
|
||||
import org.eclipse.jetty.http.DateGenerator;
|
||||
import org.eclipse.jetty.http.EtagUtils;
|
||||
import org.eclipse.jetty.http.HttpDateTime;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -364,7 +365,7 @@ public class ResourceService
|
|||
if (ifms != null && ifnm == null)
|
||||
{
|
||||
//Get jetty's Response impl
|
||||
String mdlm = content.getLastModifiedValue();
|
||||
String mdlm = DateGenerator.formatDate(content.getLastModifiedInstant());
|
||||
if (ifms.equals(mdlm))
|
||||
{
|
||||
writeHttpError(request, response, callback, HttpStatus.NOT_MODIFIED_304);
|
||||
|
@ -637,7 +638,7 @@ public class ResourceService
|
|||
response.write(true, ByteBuffer.wrap(data), callback);
|
||||
}
|
||||
|
||||
private void sendData(Request request, Response response, Callback callback, HttpContent content, List<String> reqRanges) throws IOException
|
||||
private void sendData(Request request, Response response, Callback callback, HttpContent content, List<String> reqRanges)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
|
@ -646,7 +647,6 @@ public class ResourceService
|
|||
}
|
||||
|
||||
long contentLength = content.getContentLengthValue();
|
||||
callback = Callback.from(callback, content::release);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("sendData content=%s", content));
|
||||
|
|
|
@ -839,7 +839,7 @@ public class ResourceServlet extends HttpServlet
|
|||
private static class ForcedCharacterEncodingHttpContent extends HttpContent.Wrapper
|
||||
{
|
||||
private final String characterEncoding;
|
||||
private final String contentType;
|
||||
private final HttpField contentType;
|
||||
|
||||
public ForcedCharacterEncodingHttpContent(HttpContent content, String characterEncoding)
|
||||
{
|
||||
|
@ -855,20 +855,14 @@ public class ResourceServlet extends HttpServlet
|
|||
int idx = mimeType.indexOf(";charset");
|
||||
if (idx >= 0)
|
||||
mimeType = mimeType.substring(0, idx);
|
||||
this.contentType = mimeType + ";charset=" + characterEncoding;
|
||||
this.contentType = new HttpField(HttpHeader.CONTENT_TYPE, mimeType + ";charset=" + characterEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentType()
|
||||
{
|
||||
return new HttpField(HttpHeader.CONTENT_TYPE, this.contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return this.contentType;
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -839,7 +839,7 @@ public class ResourceServlet extends HttpServlet
|
|||
private static class ForcedCharacterEncodingHttpContent extends HttpContent.Wrapper
|
||||
{
|
||||
private final String characterEncoding;
|
||||
private final String contentType;
|
||||
private final HttpField contentType;
|
||||
|
||||
public ForcedCharacterEncodingHttpContent(HttpContent content, String characterEncoding)
|
||||
{
|
||||
|
@ -855,20 +855,14 @@ public class ResourceServlet extends HttpServlet
|
|||
int idx = mimeType.indexOf(";charset");
|
||||
if (idx >= 0)
|
||||
mimeType = mimeType.substring(0, idx);
|
||||
this.contentType = mimeType + ";charset=" + characterEncoding;
|
||||
this.contentType = new HttpField(HttpHeader.CONTENT_TYPE, mimeType + ";charset=" + characterEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getContentType()
|
||||
{
|
||||
return new HttpField(HttpHeader.CONTENT_TYPE, this.contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeValue()
|
||||
{
|
||||
return this.contentType;
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,6 +33,7 @@ import jakarta.servlet.ServletException;
|
|||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.http.CompressedContentFormat;
|
||||
import org.eclipse.jetty.http.DateGenerator;
|
||||
import org.eclipse.jetty.http.EtagUtils;
|
||||
import org.eclipse.jetty.http.HttpDateTime;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -242,7 +243,6 @@ public class ResourceService
|
|||
boolean endsWithSlash = (pathInfo == null ? (_pathInfoOnly ? "" : servletPath) : pathInfo).endsWith("/");
|
||||
|
||||
HttpContent content = null;
|
||||
boolean releaseContent = true;
|
||||
try
|
||||
{
|
||||
// Find the content
|
||||
|
@ -280,7 +280,7 @@ public class ResourceService
|
|||
{
|
||||
String q = request.getQueryString();
|
||||
pathInContext = pathInContext.substring(0, pathInContext.length() - 1);
|
||||
if (q != null && q.length() != 0)
|
||||
if (q != null && !q.isEmpty())
|
||||
pathInContext += "?" + q;
|
||||
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), pathInContext)));
|
||||
return true;
|
||||
|
@ -322,7 +322,7 @@ public class ResourceService
|
|||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||
|
||||
// Send the data
|
||||
releaseContent = sendData(request, response, included, content, reqRanges);
|
||||
sendData(request, response, included, content, reqRanges);
|
||||
}
|
||||
// Can be thrown from contentFactory.getContent() call when using invalid characters
|
||||
catch (InvalidPathException e)
|
||||
|
@ -340,15 +340,6 @@ public class ResourceService
|
|||
if (!response.isCommitted())
|
||||
response.sendError(500, e.getMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (releaseContent)
|
||||
{
|
||||
if (content != null)
|
||||
content.release();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -602,7 +593,7 @@ public class ResourceService
|
|||
if (ifms != null && ifnm == null)
|
||||
{
|
||||
//Get jetty's Response impl
|
||||
String mdlm = content.getLastModifiedValue();
|
||||
String mdlm = DateGenerator.formatDate(content.getLastModifiedInstant());
|
||||
if (ifms.equals(mdlm))
|
||||
{
|
||||
sendStatus(response, HttpServletResponse.SC_NOT_MODIFIED, content::getETagValue);
|
||||
|
@ -676,7 +667,7 @@ public class ResourceService
|
|||
response.getOutputStream().write(data);
|
||||
}
|
||||
|
||||
protected boolean sendData(HttpServletRequest request,
|
||||
protected void sendData(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
boolean include,
|
||||
final HttpContent content,
|
||||
|
@ -737,7 +728,6 @@ public class ResourceService
|
|||
public void succeeded()
|
||||
{
|
||||
context.complete();
|
||||
content.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -749,7 +739,6 @@ public class ResourceService
|
|||
else
|
||||
LOG.warn(msg, x);
|
||||
context.complete();
|
||||
content.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -764,7 +753,7 @@ public class ResourceService
|
|||
return String.format("ResourceService@%x$CB", ResourceService.this.hashCode());
|
||||
}
|
||||
});
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
// otherwise write content blocking
|
||||
((HttpOutput)out).sendContent(content);
|
||||
|
@ -776,13 +765,13 @@ public class ResourceService
|
|||
List<InclusiveByteRange> ranges = InclusiveByteRange.satisfiableRanges(reqRanges, content_length);
|
||||
|
||||
// if there are no satisfiable ranges, send 416 response
|
||||
if (ranges == null || ranges.size() == 0)
|
||||
if (ranges == null || ranges.isEmpty())
|
||||
{
|
||||
response.setContentLength(0);
|
||||
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
|
||||
InclusiveByteRange.to416HeaderRangeString(content_length));
|
||||
sendStatus(response, HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, null);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is only a single valid range (must be satisfiable
|
||||
|
@ -798,7 +787,7 @@ public class ResourceService
|
|||
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
|
||||
singleSatisfiableRange.toHeaderRangeString(content_length));
|
||||
writeContent(content, out, singleSatisfiableRange.getFirst(), singleLength);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// multiple non-overlapping valid ranges cause a multipart
|
||||
|
@ -858,7 +847,6 @@ public class ResourceService
|
|||
|
||||
multi.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void writeContent(HttpContent content, OutputStream out, long start, long contentLength) throws IOException
|
||||
|
|
Loading…
Reference in New Issue