Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup DelayedDispatch handling. #9051 #12077

Open
wants to merge 59 commits into
base: jetty-12.1.x
Choose a base branch
from

Conversation

gregw
Copy link
Contributor

@gregw gregw commented Jul 23, 2024

Fix #9051. Revert to delayed content handling in the Connections

@gregw
Copy link
Contributor Author

gregw commented Jul 23, 2024

@sbordet @lorban Your thoughts on doing this? If you think it is a good idea, can you contribute the h2 and h3 delayed dispatch handling to this branch.
I will then change the DelayedHandler to only be for delaying to read the entire content (buffering to disk or parsing multi-part or form content).

@lorban
Copy link
Contributor

lorban commented Jul 23, 2024

Implementation-wise, this looks alright assuming the same logic can be replicated in H2/H3 (but I doubt this is going to be troublesome).

But could you summarize what benefits/drawbacks this has over the existing DelayedHandler both for clarity and posterity?

@gregw
Copy link
Contributor Author

gregw commented Jul 23, 2024

@lorban wrote:

could you summarize what benefits/drawbacks this has over the existing DelayedHandler both for clarity and posterity?

The DelayedHandler is not able to truly delay the dispatch to the next handler without an execute. This is because, unlike the handle call, the demand callback is serialized with other demand/write callbacks. So if a demand callback is used to call the next handler handle method, it can dead lock if blocks waiting on a demand/write callback. Thus we need to dispatch, which kind of defeats the whole purpose of avoiding scheduling delays on the initial dispatch without content.

@gregw
Copy link
Contributor Author

gregw commented Jul 26, 2024

@sbordet @lorban Can you take this one on

@gregw
Copy link
Contributor Author

gregw commented Aug 6, 2024

I've also taken the opportunity to cleanup lots of deprecation and TODOs in HttpConnection

@gregw
Copy link
Contributor Author

gregw commented Aug 7, 2024

@lachlan-roberts Can you take over the handling of delayed multipart in this PR?

@gregw
Copy link
Contributor Author

gregw commented Aug 23, 2024

@sbordet can you do the h2 handling for this
@lorban can you do the h3 handling for this

…tty-12.1.x/delayedDispatch

# Conflicts:
#	jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java
#	jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java
#	jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletTest.java
#	jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ServletTest.java
#	jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/ServletTest.java
@gregw
Copy link
Contributor Author

gregw commented Sep 15, 2024

@sbordet can you do the h2 handling for this
@lorban can you do the h3 handling for this

@gregw
Copy link
Contributor Author

gregw commented Oct 23, 2024

@lorban I can't reproduce, but the CI leak looks real enough and is definitely associated with the code that is changed in this PR.
But I cannot see how my changes could have changed how the buffers are released... other than we are now more likely to have a buffer with many retentions rather just 1 or maybe 2. So it is potentially a bad test, not waiting for an app to release??? But I can't see that also. Can you have a look at this too as we don't want to introduce a leak or a flake

@gregw
Copy link
Contributor Author

gregw commented Oct 23, 2024

@lorban actually, if you look at the full log of the CI failure, it is a write side buffer that is leaked, which is unchanged logic??? The reported releases are read side???????

@gregw gregw marked this pull request as ready for review October 24, 2024 00:14
@lorban
Copy link
Contributor

lorban commented Oct 24, 2024

@gregw the full log shows 3 leaks, as summed up by the Expected: is <0> but: was <3> failure:

first one is during write:

TrackedBuffer@17ea9c4f of 32768 bytes on 2024-10-23T07:32:30.613161227Z wrapping 1 acquired at java.lang.Throwable: qtp489567722-1119
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.<init>(ArrayByteBufferPool.java:791)
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking.acquire(ArrayByteBufferPool.java:758)
 at org.eclipse.jetty.io.ByteBufferPool$Sized.acquire(ByteBufferPool.java:162)
 at org.eclipse.jetty.ee11.servlet.HttpOutput.lockedAcquireBuffer(HttpOutput.java:610)
 at org.eclipse.jetty.ee11.servlet.HttpOutput.write(HttpOutput.java:778)
 at java.base/java.io.ByteArrayOutputStream.writeTo(ByteArrayOutputStream.java:170)
 at org.eclipse.jetty.io.WriteThroughWriter$Iso88591Writer.append(WriteThroughWriter.java:177)
 at org.eclipse.jetty.io.WriteThroughWriter.write(WriteThroughWriter.java:125)
 at org.eclipse.jetty.ee11.servlet.ResponseWriter.println(ResponseWriter.java:433)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet$1.onAllDataRead(HttpInputIntegrationTest.java:455)
 at org.eclipse.jetty.ee11.servlet.HttpInput.run(HttpInput.java:365)
 at org.eclipse.jetty.ee11.servlet.ServletChannel.lambda$handle$1(ServletChannel.java:543)
 at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.run(ContextHandler.java:1507)
 at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.run(ContextHandler.java:1500)
 at org.eclipse.jetty.ee11.servlet.ServletChannel.handle(ServletChannel.java:543)
 at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.run(ContextHandler.java:1513)
 at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.lambda$execute$0(ContextHandler.java:1530)
 at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:979)
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1209)
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1164)
 at java.base/java.lang.Thread.run(Thread.java:1575)
0 retain(s)
0 release(s)
0 over-release(s)

second one during read:

TrackedBuffer@7defa084 of 16704 bytes on 2024-10-23T07:32:30.408412567Z wrapping 1 acquired at java.lang.Throwable: qtp489567722-726
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.<init>(ArrayByteBufferPool.java:791)
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking.acquire(ArrayByteBufferPool.java:758)
 at org.eclipse.jetty.server.internal.HttpConnection.ensureRequestBuffer(HttpConnection.java:336)
 at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:356)
 at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:294)
 at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)
 at org.eclipse.jetty.io.ssl.SslConnection$SslEndPoint.onFillable(SslConnection.java:576)
 at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:391)
 at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:150)
 at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)
 at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
 at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:478)
 at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:441)
 at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:293)
 at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:201)
 at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:311)
 at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:979)
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1209)
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1164)
 at java.base/java.lang.Thread.run(Thread.java:1575)
3 retain(s)
java.lang.Throwable: Thread-836
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.retain(ArrayByteBufferPool.java:841)
 at org.eclipse.jetty.server.internal.HttpConnection$RequestHandler.content(HttpConnection.java:1003)
 at org.eclipse.jetty.http.HttpParser.parseContent(HttpParser.java:1823)
 at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1660)
 at org.eclipse.jetty.server.internal.HttpConnection.parseRequestBuffer(HttpConnection.java:597)
 at org.eclipse.jetty.server.internal.HttpConnection.parseAndFillForContent(HttpConnection.java:497)
 at org.eclipse.jetty.server.internal.HttpConnection$HttpStreamOverHTTP1.read(HttpConnection.java:1382)
 at org.eclipse.jetty.server.HttpStream$Wrapper.read(HttpStream.java:167)
 at org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest.read(HttpChannelState.java:937)
 at org.eclipse.jetty.server.Request$Wrapper.read(Request.java:858)
 at org.eclipse.jetty.server.Request$Wrapper.read(Request.java:858)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.readChunk(AsyncContentProducer.java:329)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.produceChunk(AsyncContentProducer.java:309)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.isReady(AsyncContentProducer.java:244)
 at org.eclipse.jetty.ee11.servlet.HttpInput.isReady(HttpInput.java:178)
 at org.eclipse.jetty.ee11.servlet.HttpInput.setReadListener(HttpInput.java:199)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet.lambda$doGet$0(HttpInputIntegrationTest.java:405)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest.lambda$runMode$2(HttpInputIntegrationTest.java:267)
 at java.base/java.lang.Thread.run(Thread.java:1575)
java.lang.Throwable: Thread-837
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.retain(ArrayByteBufferPool.java:841)
 at org.eclipse.jetty.server.internal.HttpConnection$RequestHandler.content(HttpConnection.java:1003)
 at org.eclipse.jetty.http.HttpParser.parseContent(HttpParser.java:1823)
 at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1660)
 at org.eclipse.jetty.server.internal.HttpConnection.parseRequestBuffer(HttpConnection.java:597)
 at org.eclipse.jetty.server.internal.HttpConnection.parseAndFillForContent(HttpConnection.java:497)
 at org.eclipse.jetty.server.internal.HttpConnection$HttpStreamOverHTTP1.read(HttpConnection.java:1382)
 at org.eclipse.jetty.server.HttpStream$Wrapper.read(HttpStream.java:167)
 at org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest.read(HttpChannelState.java:937)
 at org.eclipse.jetty.server.Request$Wrapper.read(Request.java:858)
 at org.eclipse.jetty.server.Request$Wrapper.read(Request.java:858)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.readChunk(AsyncContentProducer.java:329)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.produceChunk(AsyncContentProducer.java:309)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.isReady(AsyncContentProducer.java:244)
 at org.eclipse.jetty.ee11.servlet.HttpInput.isReady(HttpInput.java:178)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet$1.lambda$onDataAvailable$0(HttpInputIntegrationTest.java:428)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest.lambda$runMode$2(HttpInputIntegrationTest.java:267)
 at java.base/java.lang.Thread.run(Thread.java:1575)
java.lang.Throwable: Thread-837
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.retain(ArrayByteBufferPool.java:841)
 at org.eclipse.jetty.server.internal.HttpConnection$RequestHandler.content(HttpConnection.java:1003)
 at org.eclipse.jetty.http.HttpParser.parseContent(HttpParser.java:1823)
 at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1660)
 at org.eclipse.jetty.server.internal.HttpConnection.parseRequestBuffer(HttpConnection.java:597)
 at org.eclipse.jetty.server.internal.HttpConnection.parseAndFillForContent(HttpConnection.java:497)
 at org.eclipse.jetty.server.internal.HttpConnection$HttpStreamOverHTTP1.read(HttpConnection.java:1382)
 at org.eclipse.jetty.server.HttpStream$Wrapper.read(HttpStream.java:167)
 at org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest.read(HttpChannelState.java:937)
 at org.eclipse.jetty.server.Request$Wrapper.read(Request.java:858)
 at org.eclipse.jetty.server.Request$Wrapper.read(Request.java:858)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.readChunk(AsyncContentProducer.java:329)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.produceChunk(AsyncContentProducer.java:309)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.isReady(AsyncContentProducer.java:244)
 at org.eclipse.jetty.ee11.servlet.HttpInput.isReady(HttpInput.java:178)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet$1.lambda$onDataAvailable$0(HttpInputIntegrationTest.java:428)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest.lambda$runMode$2(HttpInputIntegrationTest.java:267)
 at java.base/java.lang.Thread.run(Thread.java:1575)
3 release(s)
java.lang.Throwable
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.release(ArrayByteBufferPool.java:856)
 at org.eclipse.jetty.io.internal.ByteBufferChunk$WithRetainable.release(ByteBufferChunk.java:172)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.reclaim(AsyncContentProducer.java:224)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:256)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:226)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:216)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet$1.lambda$onDataAvailable$0(HttpInputIntegrationTest.java:432)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest.lambda$runMode$2(HttpInputIntegrationTest.java:267)
 at java.base/java.lang.Thread.run(Thread.java:1575)
java.lang.Throwable
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.release(ArrayByteBufferPool.java:856)
 at org.eclipse.jetty.io.internal.ByteBufferChunk$WithRetainable.release(ByteBufferChunk.java:172)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.reclaim(AsyncContentProducer.java:224)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:256)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:226)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:216)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet$1.lambda$onDataAvailable$0(HttpInputIntegrationTest.java:432)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest.lambda$runMode$2(HttpInputIntegrationTest.java:267)
 at java.base/java.lang.Thread.run(Thread.java:1575)
java.lang.Throwable
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.release(ArrayByteBufferPool.java:856)
 at org.eclipse.jetty.io.internal.ByteBufferChunk$WithRetainable.release(ByteBufferChunk.java:172)
 at org.eclipse.jetty.ee11.servlet.AsyncContentProducer.reclaim(AsyncContentProducer.java:224)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:256)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:226)
 at org.eclipse.jetty.ee11.servlet.HttpInput.read(HttpInput.java:216)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest$TestServlet$1.lambda$onDataAvailable$0(HttpInputIntegrationTest.java:432)
 at org.eclipse.jetty.ee11.test.HttpInputIntegrationTest.lambda$runMode$2(HttpInputIntegrationTest.java:267)
 at java.base/java.lang.Thread.run(Thread.java:1575)
0 over-release(s)

third one during write again:

TrackedBuffer@192d60c2 of 8192 bytes on 2024-10-23T07:32:30.613262263Z wrapping 1 acquired at java.lang.Throwable: qtp489567722-1119
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking$TrackedBuffer.<init>(ArrayByteBufferPool.java:791)
 at org.eclipse.jetty.io.ArrayByteBufferPool$Tracking.acquire(ArrayByteBufferPool.java:758)
 at org.eclipse.jetty.server.internal.HttpConnection$SendCallback.process(HttpConnection.java:790)
 at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:377)
 at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:356)
 at org.eclipse.jetty.server.internal.HttpConnection$HttpStreamOverHTTP1.send(HttpConnection.java:1461)
 at org.eclipse.jetty.server.HttpStream$Wrapper.send(HttpStream.java:185)
 at org.eclipse.jetty.server.internal.HttpChannelState$ChannelResponse.write(HttpChannelState.java:1310)
 at org.eclipse.jetty.server.Response$Wrapper.write(Response.java:806)
 at org.eclipse.jetty.server.handler.ContextResponse.write(ContextResponse.java:56)
 at org.eclipse.jetty.ee11.servlet.ServletContextResponse.write(ServletContextResponse.java:292)
 at org.eclipse.jetty.ee11.servlet.HttpOutput.channelWrite(HttpOutput.java:204)
 at org.eclipse.jetty.ee11.servlet.HttpOutput.complete(HttpOutput.java:449)
 at org.eclipse.jetty.ee11.servlet.ServletContextResponse.completeOutput(ServletContextResponse.java:216)
 at org.eclipse.jetty.ee11.servlet.ServletChannel.handle(ServletChannel.java:577)
 at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.run(ContextHandler.java:1513)
 at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.lambda$execute$0(ContextHandler.java:1530)
 at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:979)
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1209)
 at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1164)
 at java.base/java.lang.Thread.run(Thread.java:1575)
0 retain(s)
0 release(s)
0 over-release(s)

but it looks like a flake since it cannot be reproduced, so I assume these are pre-existing bugs that were not introduced by the current PR. Fortunately the buffer pool is leak-resistant, so this is merely an annoyance.

@gregw
Copy link
Contributor Author

gregw commented Oct 24, 2024

@sbordet @lorban @lachlan-roberts assuming the leak was a flake, can I get a review to commit?

Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor changes needed, otherwise LGTM.

# Conflicts:
#	jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java
#	jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java
#	jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DelayedHandlerTest.java
@gregw
Copy link
Contributor Author

gregw commented Oct 31, 2024

@sbordet nudge?

@gregw
Copy link
Contributor Author

gregw commented Nov 3, 2024

@sbordet nudge!!!!

…tty-12.1.x/delayedDispatch

# Conflicts:
#	jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
#	jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
@gregw gregw dismissed stale reviews from sbordet and lachlan-roberts November 5, 2024 21:35

waited too long for re-review

if (!request.getConnectionMetaData().getHttpConfiguration().isDelayDispatchUntilContent())
return null;
// are we configured to delay dispatch until content?
boolean delayDispatchUntilContent = request.getConnectionMetaData().getHttpConfiguration().isDelayDispatchUntilContent();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a setting on HttpConfiguration and not the DelayedHandler?

Seems like this setting only has any effect if you add a DelayedHandler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a historic setting from jetty-7 days!
I guess we could deprecate it and just not have it. If the handler is installed, it works
@sbordet @lorban thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal preference is for this HttpConfig setting to be deprecated.

* parsed into {@link FormFields} or {@link MultiPartFormData.Parts} prior to handling.
* <p>
* This handler can allow a blocking application to run without blocking on input, as the content is asynchronously
* read before the application is called.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should specify how to set the MultiPartConfig so it is found by this handler. And how to specify the maxFormKeys and maxFormContentSize.

Because these won't be automatically set just because they have a servlet MutliPartConfigElement.

Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTTP/3 implementation is to be done.

{
// Add padding content to avoid compaction
padding = buffer.limit();
buffer.position(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some magic. This line has to know what the next line does, but I guess it has to be this way. It is just that all the times will read this line and think "why again are we setting the position to 0?".
It is what it is -- but I had to rant a little 😺

Comment on lines +6 to +7
been asynchronously read. For all other content types, the delay is for up to one
input buffer of content.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct? The implementation has a maxSize parameter that could be set to Integer.MAX_VALUE and read way more than 1 buffer.

/**
* @return The minimum space available in a retained input buffer before allocating a new one.
*/
public int getMinInputBufferSpace()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't particularly like the name, but cannot come up with a better one.

I miss the fact that this is only for retained input buffers, so perhaps getRetainedInputBufferMinSpace()?

Please also add the @ManagedAttribute annotation to the getter.

Comment on lines +61 to +62
* be {@code true} and up to {@link HttpConfiguration#getInputBufferSize()} of data may be
* {@link RetainableByteBuffer#retain() retained}. Once read, the data is made available via the standard
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the module, but see also comments about the implementation.

I'm not that keen to specify "up to inputBufferSize of data", I'd like either the first chunk, or up to the value I have configured.

};
}

protected DelayedProcess newUntilContentDelayedProcess(Handler handler, Request request, Response response, Callback callback)
{
return new UntilContentDelayedProcess(handler, request, response, callback, -1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last parameter -1 is hardcoded, so applications have no way to specify a value without subclassing, or by specifying a property in the module.

I think it should be a long and be configurable in DelayedHandler as a setter.
I was thinking that the value 0 would mean "wait until the first chunk", as well as 1.
Larger values would cap the bytes in the chunk(s).
Then, -1 could have the meaning of "read it until the last chunk", like form and multipart do.
Default would be 0, i.e. until first chunk.
I would switch to a long because 2 GiB may not that much for specific use cases.

Content.Chunk chunk = super.getRequest().read();
if (chunk == null)
{
getRequest().demand(org.eclipse.jetty.util.thread.Invocable.from(InvocationType.NON_BLOCKING, this::onContentAvailable));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be more invocations of this line than the only one time run() is called, so I would switch the 2: here call demand(this), and when it's time to call the next Handler use a lambda.

Can't this code be written with a Promise like form and multipart, and use the same AtomicInteger(2) technique instead of calling execute()?

ensureRequestBuffer();
// If there is sufficient space available, we can top up the buffer rather than allocate a new one
ByteBuffer backing = _requestBuffer.getByteBuffer();
if (BufferUtil.space(backing) >= getHttpConfiguration().getMinInputBufferSpace())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should test for minInputBufferSpace > 0 && space > minInputBufferSpace like HTTP/2 does.

// Here we know that this.networkBuffer is not retained by
// application code: either it has been released, or it's a new one.
int filled = fill(getEndPoint(), networkBuffer.getByteBuffer());
int filled = fill(getEndPoint(), networkBuffer.getByteBuffer(), compact);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In HTTP/1 we keep the buffer around if filled<=0, since it's retained anyway by the application.
Should not we do the same here?

if (filled <= 0)
{
releaseRequestBuffer();
// Keep the buffer if it is retained
if (filled < 0 || !_requestBuffer.isRetained())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't I want to keep around the buffer only if there is enough space?
So here there should be another check against minInputBufferSpace.

@@ -1724,4 +1729,97 @@ public boolean handle(Request request, Response response, Callback callback) thr
else
assertThat(response.get(HttpHeader.CONNECTION), is(expectedConnectionHeader));
}

@Test
public void testRetainedChunks() throws Exception
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should be moved to jetty-test-client-transport to test it with all transports (apart FCGI).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 👀 In review
Development

Successfully merging this pull request may close these issues.

4 participants