From b62edbf397705714f8c2a62b3de08ec84436e375 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 24 Jul 2024 14:54:20 +0200 Subject: [PATCH] cleanup and javadoc HttpContent Signed-off-by: Ludovic Orban --- .../content/CachingHttpContentFactory.java | 165 +++++------------- .../FileMappingHttpContentFactory.java | 65 ++++++- .../jetty/http/content/HttpContent.java | 163 ++++++++++------- .../content/PreCompressedHttpContent.java | 30 ---- .../http/content/ResourceHttpContent.java | 47 +---- .../eclipse/jetty/server/ResourceService.java | 6 +- .../jetty/ee10/servlet/ResourceServlet.java | 12 +- .../jetty/ee11/servlet/ResourceServlet.java | 12 +- .../jetty/ee9/nested/ResourceService.java | 30 +--- 9 files changed, 226 insertions(+), 304 deletions(-) diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java index f2c6e05c7799..5e69bb553f93 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/CachingHttpContentFactory.java @@ -31,7 +31,6 @@ 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 @@ protected void removeFromCache(CachingHttpContent content) 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 @@ protected boolean isCacheable(HttpContent httpContent) 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 HttpContent getContent(String path) throws IOException { 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 HttpContent getContent(String path) throws IOException 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 @@ protected interface CachingHttpContent extends HttpContent boolean isValid(); - boolean retain(); + void release(); } protected class CachedHttpContent extends HttpContent.Wrapper implements CachingHttpContent @@ -300,18 +304,13 @@ protected class CachedHttpContent extends HttpContent.Wrapper implements Caching private final RetainableByteBuffer _buffer; private final String _cacheKey; private final HttpField _etagField; - private final long _contentLengthValue; private volatile long _lastAccessed; private final Set _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 CachedHttpContent(String key, HttpContent httpContent) 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 void writeTo(Content.Sink sink, long offset, long length, Callback callba { 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 HttpField getETag() return _etagField; } - @Override - public String getETagValue() - { - return _etagField == null ? null : _etagField.getValue(); - } - @Override public String getCharacterEncoding() { @@ -456,27 +415,27 @@ public HttpField getContentLength() } @Override - public Instant getLastModifiedInstant() + public long getContentLengthValue() { - return _lastModifiedInstant; + return _buffer.remaining(); } @Override - public HttpField getLastModified() + public Instant getLastModifiedInstant() { - return _lastModified; + return _lastModifiedInstant; } @Override - public String getLastModifiedValue() + public HttpField getLastModified() { - return _lastModifiedValue; + return _lastModified; } @Override public boolean isValid() { - return _isValid; + return true; } } @@ -516,12 +475,6 @@ public HttpField getContentType() return null; } - @Override - public String getContentTypeValue() - { - return null; - } - @Override public String getCharacterEncoding() { @@ -540,12 +493,6 @@ public HttpField getContentEncoding() return null; } - @Override - public String getContentEncodingValue() - { - return null; - } - @Override public HttpField getContentLength() { @@ -570,24 +517,12 @@ public HttpField getLastModified() 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 boolean isValid() { return true; } - - @Override - public boolean retain() - { - return true; - } } } diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactory.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactory.java index 5e31d69120f9..21efb969a523 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactory.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/FileMappingHttpContentFactory.java @@ -16,8 +16,11 @@ 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 HttpContent getContent(String path) throws IOException 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 @@ private SingleBufferFileMappedHttpContent(HttpContent content) throws IOExceptio 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 void writeTo(Content.Sink sink, long offset, long length, Callback callba } @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 @@ private MultiBufferFileMappedHttpContent(HttpContent content, int maxBufferSize) 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 void writeTo(Content.Sink sink, long offset, long length, Callback callba { 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 @@ private int calculateEndIndex(long offset, long length) } @Override - public long getBytesOccupied() + public HttpField getContentLength() + { + return _contentLength; + } + + @Override + public long getContentLengthValue() + { + return _contentLengthValue; + } + + @Override + public Instant getLastModifiedInstant() + { + return _lastModifiedInstant; + } + + @Override + public HttpField getLastModified() { - return _bytesOccupied; + return _lastModified; } } } diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/HttpContent.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/HttpContent.java index 4920de35769a..3f44bef11a98 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/HttpContent.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/HttpContent.java @@ -19,77 +19,144 @@ 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. - *

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). + *

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). *

*/ 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(); + /** + * 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(); - Instant getLastModifiedInstant(); + /** + * Get the {@link HttpHeader#ETAG} of this HTTP content. + * + * @return the ETag, or null if this content has no ETag. + */ + HttpField getETag(); - HttpField getLastModified(); + /** + * 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(); - String getLastModifiedValue(); + /** + * 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(); - HttpField getETag(); + /** + * 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(); - String getETagValue(); + /** + * 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(); /** - *

Write a subset of this HTTP content, to a {@link Content.Sink}.

+ * 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 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 HttpField getContentType() return _delegate.getContentType(); } - @Override - public String getContentTypeValue() - { - return _delegate.getContentTypeValue(); - } - @Override public String getCharacterEncoding() { @@ -144,12 +205,6 @@ public HttpField getContentEncoding() return _delegate.getContentEncoding(); } - @Override - public String getContentEncodingValue() - { - return _delegate.getContentEncodingValue(); - } - @Override public HttpField getContentLength() { @@ -174,24 +229,12 @@ public HttpField getLastModified() 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 void writeTo(Content.Sink sink, long offset, long length, Callback callba _delegate.writeTo(sink, offset, length, callback); } - @Override - public long getBytesOccupied() - { - return _delegate.getBytesOccupied(); - } - @Override public Set getPreCompressedContentFormats() { return _delegate.getPreCompressedContentFormats(); } - @Override - public void release() - { - _delegate.release(); - } - @Override public String toString() { diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/PreCompressedHttpContent.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/PreCompressedHttpContent.java index d5e1d921bfa5..f69057c18ae7 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/PreCompressedHttpContent.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/PreCompressedHttpContent.java @@ -59,12 +59,6 @@ public HttpField getETag() return _etag; } - @Override - public String getETagValue() - { - return getETag().getValue(); - } - @Override public Instant getLastModifiedInstant() { @@ -77,36 +71,18 @@ public HttpField getLastModified() 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 Set getPreCompressedContentFormats() { return _content.getPreCompressedContentFormats(); } - - @Override - public void release() - { - _precompressedContent.release(); - } } diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ResourceHttpContent.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ResourceHttpContent.java index 8e0d4e2234ee..9df4d377718f 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ResourceHttpContent.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/content/ResourceHttpContent.java @@ -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 @@ 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 HttpField getContentEncoding() 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 HttpField getLastModified() 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 Set getPreCompressedContentFormats() { return null; } - - @Override - public void release() - { - } } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java index cc9351c9d532..1fa28a1e64c9 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java @@ -28,6 +28,7 @@ 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 @@ protected boolean passConditionalHeaders(Request request, Response response, Htt 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 @@ private void sendDirectory(Request request, Response response, HttpContent httpC response.write(true, ByteBuffer.wrap(data), callback); } - private void sendData(Request request, Response response, Callback callback, HttpContent content, List reqRanges) throws IOException + private void sendData(Request request, Response response, Callback callback, HttpContent content, List reqRanges) { if (LOG.isDebugEnabled()) { @@ -646,7 +647,6 @@ private void sendData(Request request, Response response, Callback callback, Htt } long contentLength = content.getContentLengthValue(); - callback = Callback.from(callback, content::release); if (LOG.isDebugEnabled()) LOG.debug(String.format("sendData content=%s", content)); diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ResourceServlet.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ResourceServlet.java index 245dca644b5f..e5d1c12cf430 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ResourceServlet.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ResourceServlet.java @@ -839,7 +839,7 @@ public long getContentLengthValue() 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 ForcedCharacterEncodingHttpContent(HttpContent content, String characterE 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 diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ResourceServlet.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ResourceServlet.java index dad9bd963297..dda1c55a3900 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ResourceServlet.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ResourceServlet.java @@ -839,7 +839,7 @@ public long getContentLengthValue() 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 ForcedCharacterEncodingHttpContent(HttpContent content, String characterE 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 diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java index b5951295a148..39dadbf72016 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java @@ -33,6 +33,7 @@ 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 boolean doGet(HttpServletRequest request, HttpServletResponse response) boolean endsWithSlash = (pathInfo == null ? (_pathInfoOnly ? "" : servletPath) : pathInfo).endsWith("/"); HttpContent content = null; - boolean releaseContent = true; try { // Find the content @@ -280,7 +280,7 @@ public boolean doGet(HttpServletRequest request, HttpServletResponse response) { 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 @@ else if (isGzippedContent(pathInContext)) 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 @@ else if (isGzippedContent(pathInContext)) if (!response.isCommitted()) response.sendError(500, e.getMessage()); } - finally - { - if (releaseContent) - { - if (content != null) - content.release(); - } - } - return true; } @@ -602,7 +593,7 @@ protected boolean passConditionalHeaders(HttpServletRequest request, HttpServlet 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 @@ protected void sendDirectory(HttpServletRequest request, 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 @@ else if (written) public void succeeded() { context.complete(); - content.release(); } @Override @@ -749,7 +739,6 @@ public void failed(Throwable x) else LOG.warn(msg, x); context.complete(); - content.release(); } @Override @@ -764,7 +753,7 @@ public String toString() 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 String toString() List 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 String toString() 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 String toString() multi.close(); } - return true; } private static void writeContent(HttpContent content, OutputStream out, long start, long contentLength) throws IOException