diff --git a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpChannelOverHTTP3.java b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpChannelOverHTTP3.java index 73e277ff22e..62cd1c9944e 100644 --- a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpChannelOverHTTP3.java +++ b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpChannelOverHTTP3.java @@ -174,6 +174,14 @@ public class HttpChannelOverHTTP3 extends HttpChannel public boolean onIdleTimeout(Throwable failure, Consumer consumer) { + try (AutoLock l = lock.lock()) + { + if (content == null) + content = new HttpInput.ErrorContent(failure); + else + return false; + } + boolean delayed = delayedUntilContent; delayedUntilContent = false; @@ -185,12 +193,6 @@ public class HttpChannelOverHTTP3 extends HttpChannel failure.addSuppressed(new Throwable("idle timeout")); - try (AutoLock l = lock.lock()) - { - if (content == null) - content = new HttpInput.ErrorContent(failure); - } - boolean needed = getRequest().getHttpInput().onContentProducible(); if (needed || delayed) { @@ -225,8 +227,28 @@ public class HttpChannelOverHTTP3 extends HttpChannel try (AutoLock l = lock.lock()) { if (content != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("need content has immediate content {} on {}", content, this); return true; + } } + + Stream.Data data = stream.readData(); + if (data != null) + { + HttpInput.Content result = newContent(data); + try (AutoLock l = lock.lock()) + { + if (LOG.isDebugEnabled()) + LOG.debug("need content read content {} on {}", result, this); + content = result; + } + return true; + } + + if (LOG.isDebugEnabled()) + LOG.debug("need content demanding content on {}", this); stream.demand(); return false; } @@ -254,26 +276,7 @@ public class HttpChannelOverHTTP3 extends HttpChannel if (data == null) return null; - result = new HttpInput.Content(data.getByteBuffer()) - { - @Override - public boolean isEof() - { - return data.isLast(); - } - - @Override - public void succeeded() - { - data.complete(); - } - - @Override - public void failed(Throwable x) - { - data.complete(); - } - }; + result = newContent(data); try (AutoLock l = lock.lock()) { content = result; @@ -303,6 +306,30 @@ public class HttpChannelOverHTTP3 extends HttpChannel return result; } + private HttpInput.Content newContent(Stream.Data data) + { + return new HttpInput.Content(data.getByteBuffer()) + { + @Override + public boolean isEof() + { + return data.isLast(); + } + + @Override + public void succeeded() + { + data.complete(); + } + + @Override + public void failed(Throwable x) + { + data.complete(); + } + }; + } + @Override public boolean failAllContent(Throwable failure) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingContentProducer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingContentProducer.java index 878ebcf9b3f..fb014fa7e25 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingContentProducer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingContentProducer.java @@ -171,6 +171,8 @@ class BlockingContentProducer implements ContentProducer boolean unready = _asyncContentProducer.isUnready(); if (LOG.isDebugEnabled()) LOG.debug("onContentProducible releasing semaphore {} unready={}", _semaphore, unready); + // Do not release the semaphore if we are not unready, as certain protocols may call this method + // just after having received the request, not only when they have read all the available content. if (unready) _semaphore.release(); return false; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index a60e676a1d1..998c8e7175c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -140,10 +140,11 @@ public abstract class HttpChannel implements Runnable, HttpOutput.Interceptor /** * Notify the channel that content is needed. If some content is immediately available, true is returned and * {@link #produceContent()} has to be called and will return a non-null object. - * If no content is immediately available, {@link HttpInput#onContentProducible()} is called once some content arrives - * and {@link #produceContent()} can be called without returning null. - * If a failure happens, then {@link HttpInput#onContentProducible()} will be called and an error content will return the - * error on the next call to {@link #produceContent()}. + * If no content is immediately available, an attempt to produce content must be made; if new content has been + * produced, true is returned; otherwise {@link HttpInput#onContentProducible()} is called once some content + * arrives and {@link #produceContent()} can be called without returning {@code null}. + * If a failure happens, then {@link HttpInput#onContentProducible()} will be called and an error content will + * return the error on the next call to {@link #produceContent()}. * @return true if content is immediately available. */ public abstract boolean needContent();