Skip to content

Commit

Permalink
Issue #12272 - Potential deadlock with Vaadin.
Browse files Browse the repository at this point in the history
More tests.

Signed-off-by: Simone Bordet <[email protected]>
  • Loading branch information
sbordet committed Nov 8, 2024
1 parent 279f710 commit 7b9b156
Showing 1 changed file with 78 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ public void onStartAsync(AsyncEvent event)

@ParameterizedTest
@ValueSource(booleans = {false, true})
public void testSessionShutdownWithPendingRequest(boolean useReaderWriter) throws Exception
public void testSessionCloseWithPendingRequestThenReset(boolean useReaderWriter) throws Exception
{
// Disable output aggregation for Servlets, so each byte is echoed back.
httpConfig.setOutputAggregationSize(0);
Expand All @@ -259,7 +259,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
}
});

Session session = client.connect(new InetSocketAddress("localhost", connector.getLocalPort()), new Session.Listener() {})
HTTP2Session session = (HTTP2Session)client.connect(new InetSocketAddress("localhost", connector.getLocalPort()), new Session.Listener() {})
.get(5, TimeUnit.SECONDS);
Queue<Stream.Data> dataList = new ConcurrentLinkedQueue<>();
MetaData.Request request = new MetaData.Request("POST", HttpURI.from("/"), HttpVersion.HTTP_2, HttpFields.EMPTY);
Expand Down Expand Up @@ -289,7 +289,7 @@ public void onDataAvailable(Stream stream)

await().atMost(5, TimeUnit.SECONDS).until(() -> !dataList.isEmpty());

// Initiates graceful shutdown, waits for the streams to finish as per specification.
// Initiates graceful close, waits for the streams to finish as per specification.
session.close(ErrorCode.NO_ERROR.code, "client_close", Callback.NOOP);

// Finish the pending stream, either by resetting or sending the last frame.
Expand All @@ -298,9 +298,82 @@ public void onDataAvailable(Stream stream)
// The server should see the effects of the reset.
assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
// The session must eventually be closed.
await().atMost(5, TimeUnit.SECONDS).until(() -> ((HTTP2Session)session).getCloseState() == CloseState.CLOSED);
await().atMost(5, TimeUnit.SECONDS).until(() -> session.getCloseState() == CloseState.CLOSED);
// The endPoint must eventually be closed.
await().atMost(5, TimeUnit.SECONDS).until(() -> !session.getEndPoint().isOpen());

// Cleanup.
dataList.forEach(Stream.Data::release);
}

@ParameterizedTest
@ValueSource(booleans = {false, true})
public void testSessionCloseWithPendingRequestServerIdleTimeout(boolean useReaderWriter) throws Exception
{
// Disable output aggregation for Servlets, so each byte is echoed back.
httpConfig.setOutputAggregationSize(0);
CountDownLatch serverFailureLatch = new CountDownLatch(1);
start(new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
{
try
{
if (useReaderWriter)
request.getReader().transferTo(response.getWriter());
else
request.getInputStream().transferTo(response.getOutputStream());
}
catch (Throwable x)
{
serverFailureLatch.countDown();
throw x;
}
}
});
long idleTimeout = 1000;
connector.setIdleTimeout(idleTimeout);

HTTP2Session session = (HTTP2Session)client.connect(new InetSocketAddress("localhost", connector.getLocalPort()), new Session.Listener() {})
.get(5, TimeUnit.SECONDS);
Queue<Stream.Data> dataList = new ConcurrentLinkedQueue<>();
MetaData.Request request = new MetaData.Request("POST", HttpURI.from("/"), HttpVersion.HTTP_2, HttpFields.EMPTY);
Stream stream = session.newStream(new HeadersFrame(request, null, false), new Stream.Listener()
{
@Override
public void onDataAvailable(Stream stream)
{
while (true)
{
Stream.Data data = stream.readData();
if (data == null)
{
stream.demand();
return;
}
dataList.offer(data);
if (data.frame().isEndStream())
return;
}
}
}).get(5, TimeUnit.SECONDS);

stream.data(new DataFrame(stream.getId(), UTF_8.encode("Hello Jetty"), false))
.get(5, TimeUnit.SECONDS);
stream.demand();

await().atMost(5, TimeUnit.SECONDS).until(() -> !dataList.isEmpty());

// Initiates graceful close, waits for the streams to finish as per specification.
session.close(ErrorCode.NO_ERROR.code, "client_close", Callback.NOOP);

// Do not finish the streams, the server must idle timeout.
assertTrue(serverFailureLatch.await(2 * idleTimeout, TimeUnit.SECONDS));
// The session must eventually be closed.
await().atMost(5, TimeUnit.SECONDS).until(() -> session.getCloseState() == CloseState.CLOSED);
// The endPoint must eventually be closed.
await().atMost(5, TimeUnit.SECONDS).until(() -> !((HTTP2Session)session).getEndPoint().isOpen());
await().atMost(5, TimeUnit.SECONDS).until(() -> !session.getEndPoint().isOpen());

// Cleanup.
dataList.forEach(Stream.Data::release);
Expand Down

0 comments on commit 7b9b156

Please sign in to comment.