cleanup and javadoc HttpContent (#12069)

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2024-07-25 03:59:19 +02:00 committed by GitHub
parent fb37792671
commit a9a0f164ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 227 additions and 305 deletions

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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()
{

View File

@ -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();
}
}

View File

@ -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()
{
}
}

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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