Skip to content

Issue #14403 - Defer FCGI application dispatch until the parser returns - #15648

Open
DragonFSKY wants to merge 3 commits into
jetty:jetty-12.1.xfrom
DragonFSKY:fix-14403-fcgi-dispatch
Open

Issue #14403 - Defer FCGI application dispatch until the parser returns#15648
DragonFSKY wants to merge 3 commits into
jetty:jetty-12.1.xfrom
DragonFSKY:fix-14403-fcgi-dispatch

Conversation

@DragonFSKY

@DragonFSKY DragonFSKY commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #14403.

HttpStreamOverFCGI.onHeaders() currently submits the application task from inside a ServerParser.parse() callback. A fast or inline task can therefore complete the request and release the connection input buffer before onFillable() has finished the current parser bookkeeping.

Return the task to ServerFCGIConnection and dispatch it only after ServerParser.parse() has returned and the input-buffer bookkeeping is complete. Clear the one-shot handoff before dispatch so inline execution cannot observe a stale task.

The regression test uses an inline executor to reproduce the old reentrant completion without additional thread or buffer-pool instrumentation. It fails on the target branch with the input buffer already released and passes with no tracked buffer leaks after this change.

Tests:

  • JDK 21 and JDK 25: ServerFCGIConnectionTest (1/1)
  • JDK 25: jetty-fcgi-server tests excluding ExternalFastCGIServerTest (24/24)
  • JDK 25: RoundRobinConnectionPoolTest#testMultiplexWithMaxUsage across all 6 transports
  • Spotless, Checkstyle, and Enforcer

AI disclosure

I used OpenAI GPT-5.6 and Kimi K3 to assist with issue analysis and drafting the implementation and tests. I reviewed and verified the final changes.

…returns

Signed-off-by: Dongliang Xie <dragonfsky@gmail.com>
Signed-off-by: Dongliang Xie <dragonfsky@gmail.com>

@lorban lorban left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like taking care of the TODO: here we just execute the task. in onHeaders is the actual meat of this fix, and the right thing to do: like for other protocols, wait until the parser has returned before dispatching.

The tests are head-scratching, though. They're hard to read (that's fair, race conditions are complicated to test for) seem to do a lot of unusual stuff (the finally block in testApplicationDispatchedAfterParsing() should not be needed).

Did you use AI to analyze this problem and/or generate the fix/tests? If so, you should at the very least explicitly state that. Because this PR is borderline AI slop.

{
getExecutor().execute(task);
}
catch (RejectedExecutionException x)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should never happen as the Jetty connection pool assumes the queue task is infinite. Plus, there's already a catch-all block that is supposed to do the right thing.

if (stream == null && inputBuffer.isEmpty())
releaseInputBuffer();
return;
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why breaking instead of doing the onRequest check and dispatch here? This makes the method slightly less readable.

CountDownLatch inputReleaseProceed = new CountDownLatch(1);

ArrayByteBufferPool.Tracking trackingPool = new ArrayByteBufferPool.Tracking();
ByteBufferPool bufferPool = new ByteBufferPool.Wrapper(trackingPool)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why re-wrapping the tracking pool to do a similar job to what it already does?

}
finally
{
inputReleaseProceed.countDown();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What are you trying to do here? Does that really need to be in a finally block?

finally
{
inputReleaseProceed.countDown();
fillThread.join(TimeUnit.SECONDS.toMillis(10));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the rationale behind that tiny timeout?

}

@Test
public void testApplicationDispatchRejectedBeforeRequestEnd() throws Exception

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is no need to test for that as this should never happen in practice.

}
}

private static void awaitLatch(CountDownLatch latch, String message)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is no need for this static helper, the boolean returned by latch.await() should just be tested with assertTrue().

await().atMost(10, TimeUnit.SECONDS).untilAsserted(() ->
assertThat("Server Leaks: " + trackingPool.dumpLeaks(), trackingPool.getLeaks().size(), is(0)));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All the following helpers add more confusion than value and should be inlined IMHO. It's test code after all, self-contained tests usually are more readable. There is also little value in extracting out functions that are (almost) not reused.

Signed-off-by: Dongliang Xie <dragonfsky@gmail.com>
@DragonFSKY

Copy link
Copy Markdown
Contributor Author

Yes, I did use an AI coding assistant here, and I didn’t review its output carefully enough before opening the PR. That was my mistake, and I’m sorry for creating unnecessary review work for you. I’ve now removed the unnecessary rejection handling and test scaffolding. I’ll make sure future changes are carefully reviewed, simplified, and consistent with the existing code and test style before I submit them.

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.

RoundRobinConnectionPoolTest.testMultiplexWithMaxUsage() on FCGI is flaky

3 participants