diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContentProducer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContentProducer.java index 114f6f3fd3e..23bd2aa37ba 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContentProducer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContentProducer.java @@ -324,33 +324,35 @@ class AsyncContentProducer implements ContentProducer while (true) { - if (_transformedContent != null && (_transformedContent.isSpecial() || !_transformedContent.isEmpty())) + if (_transformedContent != null) { - if (_transformedContent.isSpecial() && !_error) + if (_transformedContent.isSpecial() || !_transformedContent.isEmpty()) { - // In case the _rawContent was set by consumeAll(), check the httpChannel - // to see if it has a more precise error. Otherwise, the exact same - // special content will be returned by the httpChannel; do not do that - // if the _error flag was set, meaning the current error is definitive. - HttpInput.Content refreshedRawContent = produceRawContent(); - if (refreshedRawContent != null) - _rawContent = _transformedContent = refreshedRawContent; - _error = _rawContent.getError() != null; + if (_transformedContent.isSpecial() && !_error) + { + // In case the _rawContent was set by consumeAll(), check the httpChannel + // to see if it has a more precise error. Otherwise, the exact same + // special content will be returned by the httpChannel; do not do that + // if the _error flag was set, meaning the current error is definitive. + HttpInput.Content refreshedRawContent = produceRawContent(); + if (refreshedRawContent != null) + _rawContent = _transformedContent = refreshedRawContent; + _error = _rawContent.getError() != null; + if (LOG.isDebugEnabled()) + LOG.debug("refreshed raw content: {} {}", _rawContent, this); + } + if (LOG.isDebugEnabled()) - LOG.debug("refreshed raw content: {} {}", _rawContent, this); + LOG.debug("transformed content not yet depleted, returning it {}", this); + return _transformedContent; + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("current transformed content depleted {}", this); + _transformedContent.succeeded(); + _transformedContent = null; } - - if (LOG.isDebugEnabled()) - LOG.debug("transformed content not yet depleted, returning it {}", this); - return _transformedContent; - } - - if (_transformedContent != null && _transformedContent.isEmpty() && !_transformedContent.isSpecial()) - { - if (LOG.isDebugEnabled()) - LOG.debug("current transformed content depleted {}", this); - _transformedContent.succeeded(); - _transformedContent = null; } if (_rawContent == null) @@ -390,7 +392,7 @@ class AsyncContentProducer implements ContentProducer return; } - // If the interceptor generated a null content, recycle the raw content now if its empty. + // If the interceptor generated a null content, recycle the raw content now if it is empty. if (_transformedContent == null && _rawContent.isEmpty() && !_rawContent.isSpecial()) { if (LOG.isDebugEnabled()) @@ -400,14 +402,13 @@ class AsyncContentProducer implements ContentProducer return; } - // If the interceptor returned the raw content, recycle the raw content now if its empty. + // If the interceptor returned the raw content, recycle the raw content now if it is empty. if (_transformedContent == _rawContent && _rawContent.isEmpty() && !_rawContent.isSpecial()) { if (LOG.isDebugEnabled()) LOG.debug("interceptor returned the raw content, recycle the empty raw content now {}", this); _rawContent.succeeded(); _rawContent = _transformedContent = null; - return; } } else diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index 0c8b96c6e84..fe66f75d559 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -420,7 +420,7 @@ public class HttpInput extends ServletInputStream implements Runnable * When {@link Content} instances are generated, they are passed to the registered interceptor (if any) * that is then responsible for providing the actual content that is consumed by {@link #read(byte[], int, int)} and its * sibling methods.

- *

A minimal implementation could be as simple as: + * A minimal implementation could be as simple as: *

      * public HttpInput.Content readFrom(HttpInput.Content content)
      * {
@@ -453,11 +453,11 @@ public class HttpInput extends ServletInputStream implements Runnable
      *     occur only after the contained byte buffer is empty (see above) or at any time if the returned content was special.
      *     
  • Once {@link #readFrom(Content)} returned a special content, subsequent calls to {@link #readFrom(Content)} must * always return the same special content.
  • + *
  • When {@link #readFrom(Content)} gets passed a non-special content, it must either return the content it was + * passed or fully consume the contained byte buffer.
  • *
  • Implementations implementing both this interface and {@link Destroyable} will have their * {@link Destroyable#destroy()} method called when {@link #recycle()} is called.
  • * - *

    - *

    * @see org.eclipse.jetty.server.handler.gzip.GzipHttpInputInterceptor */ public interface Interceptor @@ -612,7 +612,9 @@ public class HttpInput extends ServletInputStream implements Runnable } /** - * Check if the content is special. + * Check if the content is special. A content is deemed special + * if it does not hold bytes but rather conveys a special event, + * like when EOF has been reached or an error has occurred. * @return true if the content is special, false otherwise. */ public boolean isSpecial() diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingContentProducerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingContentProducerTest.java index 983a793f4fc..63c976380aa 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingContentProducerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingContentProducerTest.java @@ -136,14 +136,14 @@ public class BlockingContentProducerTest contentProducer.setInterceptor(content -> new HttpInput.ErrorContent(new Throwable("testBlockingContentProducerInterceptorGeneratesError interceptor error"))); HttpInput.Content content1 = contentProducer.nextContent(); - assertThat(content1.isSpecial(), Matchers.is(true)); - assertThat(content1.getError().getMessage(), Matchers.is("testBlockingContentProducerInterceptorGeneratesError interceptor error")); + assertThat(content1.isSpecial(), is(true)); + assertThat(content1.getError().getMessage(), is("testBlockingContentProducerInterceptorGeneratesError interceptor error")); HttpInput.Content content2 = contentProducer.nextContent(); - assertThat(content2.isSpecial(), Matchers.is(true)); - assertThat(content2.getError().getMessage(), Matchers.is("testBlockingContentProducerInterceptorGeneratesError interceptor error")); + assertThat(content2.isSpecial(), is(true)); + assertThat(content2.getError().getMessage(), is("testBlockingContentProducerInterceptorGeneratesError interceptor error")); } - assertThat(contentSucceededCount.get(), Matchers.is(1)); + assertThat(contentSucceededCount.get(), is(1)); } @Test @@ -163,14 +163,14 @@ public class BlockingContentProducerTest contentProducer.setInterceptor(content -> new HttpInput.EofContent()); HttpInput.Content content1 = contentProducer.nextContent(); - assertThat(content1.isSpecial(), Matchers.is(true)); - assertThat(content1.isEof(), Matchers.is(true)); + assertThat(content1.isSpecial(), is(true)); + assertThat(content1.isEof(), is(true)); HttpInput.Content content2 = contentProducer.nextContent(); - assertThat(content2.isSpecial(), Matchers.is(true)); - assertThat(content2.isEof(), Matchers.is(true)); + assertThat(content2.isSpecial(), is(true)); + assertThat(content2.isEof(), is(true)); } - assertThat(contentSucceededCount.get(), Matchers.is(1)); + assertThat(contentSucceededCount.get(), is(1)); } @Test @@ -193,14 +193,14 @@ public class BlockingContentProducerTest }); HttpInput.Content content1 = contentProducer.nextContent(); - assertThat(content1.isSpecial(), Matchers.is(true)); - assertThat(content1.getError().getCause().getMessage(), Matchers.is("testBlockingContentProducerInterceptorThrows error")); + assertThat(content1.isSpecial(), is(true)); + assertThat(content1.getError().getCause().getMessage(), is("testBlockingContentProducerInterceptorThrows error")); HttpInput.Content content2 = contentProducer.nextContent(); - assertThat(content2.isSpecial(), Matchers.is(true)); - assertThat(content1.getError().getCause().getMessage(), Matchers.is("testBlockingContentProducerInterceptorThrows error")); + assertThat(content2.isSpecial(), is(true)); + assertThat(content1.getError().getCause().getMessage(), is("testBlockingContentProducerInterceptorThrows error")); } - assertThat(contentFailedCount.get(), Matchers.is(1)); + assertThat(contentFailedCount.get(), is(1)); } @Test @@ -226,16 +226,16 @@ public class BlockingContentProducerTest assertThat(error, nullValue()); HttpInput.Content lastContent = contentProducer.nextContent(); - assertThat(lastContent.isSpecial(), Matchers.is(true)); - assertThat(lastContent.isEof(), Matchers.is(true)); + assertThat(lastContent.isSpecial(), is(true)); + assertThat(lastContent.isEof(), is(true)); } - assertThat(interceptor.contents.size(), Matchers.is(4)); - assertThat(interceptor.contents.get(0).isSpecial(), Matchers.is(false)); - assertThat(interceptor.contents.get(1).isSpecial(), Matchers.is(false)); - assertThat(interceptor.contents.get(2).isSpecial(), Matchers.is(false)); - assertThat(interceptor.contents.get(3).isSpecial(), Matchers.is(true)); - assertThat(interceptor.contents.get(3).isEof(), Matchers.is(true)); + assertThat(interceptor.contents.size(), is(4)); + assertThat(interceptor.contents.get(0).isSpecial(), is(false)); + assertThat(interceptor.contents.get(1).isSpecial(), is(false)); + assertThat(interceptor.contents.get(2).isSpecial(), is(false)); + assertThat(interceptor.contents.get(3).isSpecial(), is(true)); + assertThat(interceptor.contents.get(3).isEof(), is(true)); } @Test @@ -258,19 +258,19 @@ public class BlockingContentProducerTest contentProducer.setInterceptor(interceptor); Throwable error = readAndAssertContent(totalContentBytesCount, originalContentString, buffers.length + 1, contentProducer); - assertThat(error.getMessage(), Matchers.is("testBlockingContentProducerErrorContentIsPassedToInterceptor error")); + assertThat(error.getMessage(), is("testBlockingContentProducerErrorContentIsPassedToInterceptor error")); HttpInput.Content lastContent = contentProducer.nextContent(); - assertThat(lastContent.isSpecial(), Matchers.is(true)); - assertThat(lastContent.getError().getMessage(), Matchers.is("testBlockingContentProducerErrorContentIsPassedToInterceptor error")); + assertThat(lastContent.isSpecial(), is(true)); + assertThat(lastContent.getError().getMessage(), is("testBlockingContentProducerErrorContentIsPassedToInterceptor error")); } - assertThat(interceptor.contents.size(), Matchers.is(4)); - assertThat(interceptor.contents.get(0).isSpecial(), Matchers.is(false)); - assertThat(interceptor.contents.get(1).isSpecial(), Matchers.is(false)); - assertThat(interceptor.contents.get(2).isSpecial(), Matchers.is(false)); - assertThat(interceptor.contents.get(3).isSpecial(), Matchers.is(true)); - assertThat(interceptor.contents.get(3).getError().getMessage(), Matchers.is("testBlockingContentProducerErrorContentIsPassedToInterceptor error")); + assertThat(interceptor.contents.size(), is(4)); + assertThat(interceptor.contents.get(0).isSpecial(), is(false)); + assertThat(interceptor.contents.get(1).isSpecial(), is(false)); + assertThat(interceptor.contents.get(2).isSpecial(), is(false)); + assertThat(interceptor.contents.get(3).isSpecial(), is(true)); + assertThat(interceptor.contents.get(3).getError().getMessage(), is("testBlockingContentProducerErrorContentIsPassedToInterceptor error")); } @Test