#7284 cleanup HttpInput reopen/recycle

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2021-12-15 11:31:18 +01:00
parent c7cec39ff5
commit 44fb63e541
4 changed files with 48 additions and 6 deletions

View File

@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
class AsyncContentProducer implements ContentProducer
{
private static final Logger LOG = LoggerFactory.getLogger(AsyncContentProducer.class);
private static final HttpInput.ErrorContent RECYCLED_ERROR_CONTENT = new HttpInput.ErrorContent(new IllegalStateException("ContentProducer has been recycled"));
private static final Throwable UNCONSUMED_CONTENT_EXCEPTION = new IOException("Unconsumed content")
{
@Override
@ -66,9 +67,30 @@ class AsyncContentProducer implements ContentProducer
assertLocked();
if (LOG.isDebugEnabled())
LOG.debug("recycling {}", this);
// Make sure that the content has been fully consumed before destroying the interceptor and also make sure
// that asking this instance for content between recycle and reopen will only produce error'ed content.
if (_rawContent == null)
_rawContent = RECYCLED_ERROR_CONTENT;
else if (!_rawContent.isSpecial())
throw new IllegalStateException("ContentProducer with unconsumed content cannot be recycled");
if (_transformedContent == null)
_transformedContent = RECYCLED_ERROR_CONTENT;
else if (!_transformedContent.isSpecial())
throw new IllegalStateException("ContentProducer with unconsumed content cannot be recycled");
if (_interceptor instanceof Destroyable)
((Destroyable)_interceptor).destroy();
_interceptor = null;
}
@Override
public void reopen()
{
assertLocked();
if (LOG.isDebugEnabled())
LOG.debug("reopening {}", this);
_rawContent = null;
_transformedContent = null;
_error = false;

View File

@ -46,6 +46,14 @@ class BlockingContentProducer implements ContentProducer
if (LOG.isDebugEnabled())
LOG.debug("recycling {}", this);
_asyncContentProducer.recycle();
}
@Override
public void reopen()
{
if (LOG.isDebugEnabled())
LOG.debug("reopening {}", this);
_asyncContentProducer.reopen();
_semaphore.drainPermits();
}

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.server;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.thread.AutoLock;
/**
@ -27,17 +28,24 @@ import org.eclipse.jetty.util.thread.AutoLock;
public interface ContentProducer
{
/**
* Lock this instance. The lock must be held before any method of this instance's
* method be called, and must be manually released afterward.
* Lock this instance. The lock must be held before any of this instance's
* method can be called, and must be released afterward.
* @return the lock that is guarding this instance.
*/
AutoLock lock();
/**
* Reset all internal state and clear any held resources.
* Clear the interceptor and call {@link Destroyable#destroy()} on it if it implements {@link Destroyable}.
* A recycled {@link ContentProducer} will only produce special content with a non-null error until
* {@link #reopen()} is called.
*/
void recycle();
/**
* Reset all internal state, making this is instance logically equivalent to a freshly allocated one.
*/
void reopen();
/**
* Fail all content currently available in this {@link ContentProducer} instance
* as well as in the underlying {@link HttpChannel}.

View File

@ -55,8 +55,12 @@ public class HttpInput extends ServletInputStream implements Runnable
public void recycle()
{
if (LOG.isDebugEnabled())
LOG.debug("recycle {}", this);
try (AutoLock lock = _contentProducer.lock())
{
if (LOG.isDebugEnabled())
LOG.debug("recycle {}", this);
_blockingContentProducer.recycle();
}
}
public void reopen()
@ -65,7 +69,7 @@ public class HttpInput extends ServletInputStream implements Runnable
{
if (LOG.isDebugEnabled())
LOG.debug("reopen {}", this);
_blockingContentProducer.recycle();
_blockingContentProducer.reopen();
_contentProducer = _blockingContentProducer;
_consumedEof = false;
_readListener = null;