From 97f9c1d0fbf1b3c2ccd6488c35b63e7265e6c6eb Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Mon, 10 Jun 2024 13:36:31 +0200
Subject: [PATCH 01/22] fix missing notifyRemoteAsyncErrors http config wiring
Signed-off-by: Ludovic Orban
---
.../org/eclipse/jetty/ee10/servlet/ServletChannelState.java | 3 ++-
.../java/org/eclipse/jetty/ee9/nested/HttpChannelState.java | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
index 0c419cba5598..46f1fe1050bb 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
@@ -572,7 +572,8 @@ public void startAsync(AsyncContextEvent event)
if (!_failureListener)
{
_failureListener = true;
- _servletChannel.getRequest().addFailureListener(this::asyncError);
+ if (_servletChannel.getHttpConfiguration().isNotifyRemoteAsyncErrors())
+ _servletChannel.getRequest().addFailureListener(this::asyncError);
}
_requestState = RequestState.ASYNC;
_event = event;
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
index 7fdc4184b068..24659120a531 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
@@ -534,7 +534,8 @@ public void startAsync(AsyncContextEvent event)
if (!_failureListener)
{
_failureListener = true;
- getHttpChannel().getCoreRequest().addFailureListener(this::asyncError);
+ if (_channel.getHttpConfiguration().isNotifyRemoteAsyncErrors())
+ getHttpChannel().getCoreRequest().addFailureListener(this::asyncError);
}
_requestState = RequestState.ASYNC;
_event = event;
From f10b2458920d09951f766a2dd7df9066709f97be Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 11 Jun 2024 12:13:28 +0200
Subject: [PATCH 02/22] add tests
Signed-off-by: Ludovic Orban
---
.../jetty/http2/tests/AbstractTest.java | 7 +-
.../jetty/http2/tests/AsyncIOTest.java | 71 +++++++++++++++
.../jetty/http2/tests/AsyncServletTest.java | 2 +
.../server/internal/HttpChannelState.java | 2 +-
.../jetty/ee10/servlet/HttpOutput.java | 4 +-
.../test/client/transport/AbstractTest.java | 35 +++++++-
.../client/transport/AsyncIOServletTest.java | 87 ++++++++++++++++++
.../test/client/transport/AbstractTest.java | 35 +++++++-
.../client/transport/AsyncIOServletTest.java | 90 +++++++++++++++++++
9 files changed, 328 insertions(+), 5 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractTest.java
index 90bc346b4ce9..9a5f944f14da 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AbstractTest.java
@@ -58,7 +58,12 @@ public class AbstractTest
protected void start(Handler handler) throws Exception
{
- HTTP2CServerConnectionFactory connectionFactory = new HTTP2CServerConnectionFactory(new HttpConfiguration());
+ start(handler, new HttpConfiguration());
+ }
+
+ protected void start(Handler handler, HttpConfiguration httpConfiguration) throws Exception
+ {
+ HTTP2CServerConnectionFactory connectionFactory = new HTTP2CServerConnectionFactory(httpConfiguration);
connectionFactory.setInitialSessionRecvWindow(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
connectionFactory.setInitialStreamRecvWindow(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
prepareServer(connectionFactory);
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
index 430d721bcf7c..0be119895252 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
@@ -17,22 +17,33 @@
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@@ -241,6 +252,66 @@ public void onClosed(Stream stream)
*/
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testClientResetWithoutRemoteErrorNotification(boolean notify) throws Exception
+ {
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference responseRef = new AtomicReference<>();
+ AtomicReference failureRef = new AtomicReference<>();
+ HttpConfiguration httpConfiguration = new HttpConfiguration();
+ httpConfiguration.setNotifyRemoteAsyncErrors(notify);
+ start(new Handler.Abstract() {
+ @Override
+ public boolean handle(Request request, Response response, Callback callback)
+ {
+ request.addFailureListener(failureRef::set);
+ responseRef.set(response);
+ latch.countDown();
+ return true;
+ }
+ }, httpConfiguration);
+
+ Session session = newClientSession(new Session.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, true);
+ FuturePromise promise = new FuturePromise<>();
+ session.newStream(frame, promise, null);
+ Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be idle.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ sleep(500);
+
+ stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+
+ if (notify)
+ // Wait for the reset to be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).until(failureRef::get, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(failureRef::get, nullValue());
+
+ // Assert that writing to the response fails.
+ var cb = new Callback()
+ {
+ private Throwable failure = null;
+
+ @Override
+ public void failed(Throwable x)
+ {
+ failure = x;
+ }
+
+ Throwable failure()
+ {
+ return failure;
+ }
+ };
+ responseRef.get().write(true, BufferUtil.EMPTY_BUFFER, cb);
+ await().atMost(5, TimeUnit.SECONDS).until(cb::failure, instanceOf(EofException.class));
+ }
+
private static void sleep(long ms) throws InterruptedIOException
{
try
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java
index 328e21571962..898987c72f7e 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java
@@ -154,6 +154,7 @@ public void test()
// assertTrue(clientLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
// }
//
+
// @Test
// public void testStartAsyncThenClientResetWithoutRemoteErrorNotification() throws Exception
// {
@@ -206,6 +207,7 @@ public void test()
// output.flush();
// });
// }
+
//
// @Test
// public void testStartAsyncThenServerSessionIdleTimeout() throws Exception
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
index dc2b3be55d62..7d7ab8cc380b 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
@@ -437,7 +437,7 @@ public Runnable onFailure(Throwable x)
// Notify the failure listeners only once.
Consumer onFailure = _onFailure;
_onFailure = null;
- Runnable invokeOnFailureListeners = onFailure == null ? null : () ->
+ Runnable invokeOnFailureListeners = onFailure == null || !getHttpConfiguration().isNotifyRemoteAsyncErrors() ? null : () ->
{
try
{
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java
index 419fc0d9ebf5..b9e6270401d7 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java
@@ -676,7 +676,9 @@ public void flush() throws IOException
catch (Throwable t)
{
onWriteComplete(false, t);
- throw t;
+ if (t instanceof IOException)
+ throw t;
+ throw new IOException(t);
}
}
}
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
index 806ada98f318..bf137ebe316f 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
@@ -14,6 +14,7 @@
package org.eclipse.jetty.ee10.test.client.transport;
import java.io.InputStream;
+import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -21,6 +22,7 @@
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import jakarta.servlet.http.HttpServlet;
@@ -32,7 +34,13 @@
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI;
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
@@ -58,6 +66,7 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -79,6 +88,7 @@ public class AbstractTest
protected AbstractConnector connector;
protected ServletContextHandler servletContextHandler;
protected HttpClient client;
+ protected HTTP2Client http2Client;
public static Collection transports()
{
@@ -189,6 +199,29 @@ protected void startClient(Transport transport, Consumer consumer) t
client.start();
}
+ protected Session newHttp2ClientSession(Session.Listener listener) throws Exception
+ {
+ String host = "localhost";
+ int port = ((NetworkConnector)connector).getLocalPort();
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ FuturePromise promise = new FuturePromise<>();
+ http2Client.connect(address, listener, promise);
+ return promise.get(5, TimeUnit.SECONDS);
+ }
+
+ protected MetaData.Request newRequest(String method, HttpFields fields)
+ {
+ return newRequest(method, "/", fields);
+ }
+
+ protected MetaData.Request newRequest(String method, String path, HttpFields fields)
+ {
+ String host = "localhost";
+ int port = ((NetworkConnector)connector).getLocalPort();
+ String authority = host + ":" + port;
+ return new MetaData.Request(method, HttpScheme.HTTP.asString(), new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields, -1);
+ }
+
public AbstractConnector newConnector(Transport transport, Server server)
{
return switch (transport)
@@ -262,7 +295,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(newSslContextFactoryClient());
- HTTP2Client http2Client = new HTTP2Client(clientConnector);
+ http2Client = new HTTP2Client(clientConnector);
yield new HttpClientTransportOverHTTP2(http2Client);
}
case H3 ->
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
index 41472d1fd7c0..642bca937a3e 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
@@ -34,6 +34,8 @@
import java.util.concurrent.atomic.AtomicReference;
import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
@@ -54,13 +56,19 @@
import org.eclipse.jetty.client.StringRequestContent;
import org.eclipse.jetty.client.transport.internal.HttpConnectionOverHTTP;
import org.eclipse.jetty.ee10.servlet.HttpOutput;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.client.transport.internal.HttpConnectionOverHTTP2;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.logging.StacklessLogging;
@@ -76,6 +84,7 @@
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -83,6 +92,7 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -1534,6 +1544,83 @@ public void onError(Throwable x)
assertEquals(HttpStatus.NO_CONTENT_204, response.getStatus());
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testStartAsyncThenClientResetWithoutRemoteErrorNotification(boolean notify) throws Exception
+ {
+ httpConfig.setNotifyRemoteAsyncErrors(notify);
+
+ AtomicReference errorAsyncEventRef = new AtomicReference<>();
+ AtomicReference responseRef = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ start(Transport.H2C, new HttpServlet() {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener() {
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ errorAsyncEventRef.set(event);
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ }
+ });
+ asyncContext.setTimeout(0);
+ responseRef.set(response);
+ latch.countDown();
+ }
+ });
+
+ Session session = newHttp2ClientSession(new Session.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, true);
+ FuturePromise promise = new FuturePromise<>();
+ session.newStream(frame, promise, null);
+ Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be in ASYNC_WAIT.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ sleep(500);
+
+ stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+
+ if (notify)
+ // Wait for the reset to be notified to the async context listener.
+ await().atMost(5, TimeUnit.SECONDS).until(() ->
+ {
+ AsyncEvent asyncEvent = errorAsyncEventRef.get();
+ return asyncEvent == null ? null : asyncEvent.getThrowable();
+ }, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
+
+ ServletOutputStream output = responseRef.get().getOutputStream();
+
+ assertThrows(IOException.class,
+ () ->
+ {
+ // Large writes or explicit flush() must
+ // fail because the stream has been reset.
+ output.flush();
+ });
+ }
+
private static class Listener implements ReadListener, WriteListener
{
private final Executor executor = Executors.newFixedThreadPool(32);
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
index 63efe5877d06..e7e3b81d429e 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
@@ -15,6 +15,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -26,6 +27,7 @@
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServlet;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
@@ -36,7 +38,13 @@
import org.eclipse.jetty.ee9.servlet.ServletHolder;
import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI;
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
@@ -62,6 +70,7 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -83,6 +92,7 @@ public class AbstractTest
protected AbstractConnector connector;
protected ServletContextHandler servletContextHandler;
protected HttpClient client;
+ protected HTTP2Client http2Client;
public static Collection transports()
{
@@ -196,6 +206,29 @@ protected void startClient(Transport transport) throws Exception
client.start();
}
+ protected Session newHttp2ClientSession(Session.Listener listener) throws Exception
+ {
+ String host = "localhost";
+ int port = ((NetworkConnector)connector).getLocalPort();
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ FuturePromise promise = new FuturePromise<>();
+ http2Client.connect(address, listener, promise);
+ return promise.get(5, TimeUnit.SECONDS);
+ }
+
+ protected MetaData.Request newRequest(String method, HttpFields fields)
+ {
+ return newRequest(method, "/", fields);
+ }
+
+ protected MetaData.Request newRequest(String method, String path, HttpFields fields)
+ {
+ String host = "localhost";
+ int port = ((NetworkConnector)connector).getLocalPort();
+ String authority = host + ":" + port;
+ return new MetaData.Request(method, HttpScheme.HTTP.asString(), new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields, -1);
+ }
+
public AbstractConnector newConnector(Transport transport, Server server)
{
return switch (transport)
@@ -268,7 +301,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(newSslContextFactoryClient());
- HTTP2Client http2Client = new HTTP2Client(clientConnector);
+ http2Client = new HTTP2Client(clientConnector);
yield new HttpClientTransportOverHTTP2(http2Client);
}
case H3 ->
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
index 4a10ca2b01fe..08cfcbaec522 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
@@ -35,6 +35,8 @@
import java.util.concurrent.atomic.AtomicReference;
import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
@@ -56,13 +58,19 @@
import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.nested.HttpInput;
import org.eclipse.jetty.ee9.nested.HttpOutput;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.client.transport.internal.HttpConnectionOverHTTP2;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.logging.StacklessLogging;
@@ -76,6 +84,7 @@
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
import static java.nio.ByteBuffer.wrap;
import static org.awaitility.Awaitility.await;
@@ -85,6 +94,7 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -1861,6 +1871,86 @@ public void onError(Throwable x)
assertEquals(1, allDataReadCount.get());
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testStartAsyncThenClientResetWithoutRemoteErrorNotification(boolean notify) throws Exception
+ {
+ httpConfig.setNotifyRemoteAsyncErrors(notify);
+
+ AtomicReference errorAsyncEventRef = new AtomicReference<>();
+ AtomicReference responseRef = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ start(Transport.H2C, new HttpServlet() {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener() {
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ errorAsyncEventRef.set(event);
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ }
+ });
+ asyncContext.setTimeout(0);
+ responseRef.set(response);
+ latch.countDown();
+ }
+ });
+
+ Session session = newHttp2ClientSession(new Session.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, true);
+ FuturePromise promise = new FuturePromise<>();
+ session.newStream(frame, promise, null);
+ Stream stream = promise.get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be in ASYNC_WAIT.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ sleep(500);
+
+ stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+
+ if (notify)
+ // Wait for the reset to be notified to the async context listener.
+ await().atMost(5, TimeUnit.SECONDS).until(() ->
+ {
+ AsyncEvent asyncEvent = errorAsyncEventRef.get();
+ return asyncEvent == null ? null : asyncEvent.getThrowable();
+ }, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
+
+ ServletOutputStream output = responseRef.get().getOutputStream();
+
+ if (notify)
+ output.flush();
+ else
+ assertThrows(IOException.class,
+ () ->
+ {
+ // Large writes or explicit flush() must
+ // fail because the stream has been reset.
+ output.flush();
+ });
+ }
+
private static class Listener implements ReadListener, WriteListener
{
private final Executor executor = Executors.newFixedThreadPool(32);
From 294357716c51d8495353c7f4fda5b221840daeea Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 11 Jun 2024 12:14:48 +0200
Subject: [PATCH 03/22] remove ported test
Signed-off-by: Ludovic Orban
---
.../jetty/http2/tests/AsyncServletTest.java | 54 -------------------
1 file changed, 54 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java
index 898987c72f7e..5ca981b723b3 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncServletTest.java
@@ -154,60 +154,6 @@ public void test()
// assertTrue(clientLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
// }
//
-
-// @Test
-// public void testStartAsyncThenClientResetWithoutRemoteErrorNotification() throws Exception
-// {
-// HttpConfiguration httpConfiguration = new HttpConfiguration();
-// httpConfiguration.setNotifyRemoteAsyncErrors(false);
-// prepareServer(new HTTP2ServerConnectionFactory(httpConfiguration));
-// ServletContextHandler context = new ServletContextHandler(server, "/");
-// AtomicReference asyncContextRef = new AtomicReference<>();
-// CountDownLatch latch = new CountDownLatch(1);
-// context.addServlet(new ServletHolder(new HttpServlet()
-// {
-// @Override
-// protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-// {
-// AsyncContext asyncContext = request.startAsync();
-// asyncContext.setTimeout(0);
-// asyncContextRef.set(asyncContext);
-// latch.countDown();
-// }
-// }), servletPath + "/*");
-// server.start();
-//
-// prepareClient();
-// client.start();
-// Session session = newClient(new Session.Listener() {});
-// MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
-// HeadersFrame frame = new HeadersFrame(metaData, null, true);
-// FuturePromise promise = new FuturePromise<>();
-// session.newStream(frame, promise, null);
-// Stream stream = promise.get(5, TimeUnit.SECONDS);
-//
-// // Wait for the server to be in ASYNC_WAIT.
-// assertTrue(latch.await(5, TimeUnit.SECONDS));
-// sleep(500);
-//
-// stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
-//
-// // Wait for the reset to be processed by the server.
-// sleep(500);
-//
-// AsyncContext asyncContext = asyncContextRef.get();
-// ServletResponse response = asyncContext.getResponse();
-// ServletOutputStream output = response.getOutputStream();
-//
-// assertThrows(IOException.class,
-// () ->
-// {
-// // Large writes or explicit flush() must
-// // fail because the stream has been reset.
-// output.flush();
-// });
-// }
-
//
// @Test
// public void testStartAsyncThenServerSessionIdleTimeout() throws Exception
From 9fbae2a8e33b7cc50cccccad5314581e53c04ebf Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 11 Jun 2024 14:33:01 +0200
Subject: [PATCH 04/22] fix checkstyle
Signed-off-by: Ludovic Orban
---
.../org/eclipse/jetty/http2/tests/AsyncIOTest.java | 3 ++-
.../test/client/transport/AsyncIOServletTest.java | 14 ++++++++------
.../test/client/transport/AsyncIOServletTest.java | 14 ++++++++------
3 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
index 0be119895252..0c45757bdb21 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
@@ -261,7 +261,8 @@ public void testClientResetWithoutRemoteErrorNotification(boolean notify) throws
AtomicReference failureRef = new AtomicReference<>();
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setNotifyRemoteAsyncErrors(notify);
- start(new Handler.Abstract() {
+ start(new Handler.Abstract()
+ {
@Override
public boolean handle(Request request, Response response, Callback callback)
{
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
index 642bca937a3e..34562151bae1 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
@@ -1553,30 +1553,32 @@ public void testStartAsyncThenClientResetWithoutRemoteErrorNotification(boolean
AtomicReference errorAsyncEventRef = new AtomicReference<>();
AtomicReference responseRef = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
- start(Transport.H2C, new HttpServlet() {
+ start(Transport.H2C, new HttpServlet()
+ {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
{
AsyncContext asyncContext = request.startAsync();
- asyncContext.addListener(new AsyncListener() {
+ asyncContext.addListener(new AsyncListener()
+ {
@Override
- public void onComplete(AsyncEvent event) throws IOException
+ public void onComplete(AsyncEvent event)
{
}
@Override
- public void onTimeout(AsyncEvent event) throws IOException
+ public void onTimeout(AsyncEvent event)
{
}
@Override
- public void onError(AsyncEvent event) throws IOException
+ public void onError(AsyncEvent event)
{
errorAsyncEventRef.set(event);
}
@Override
- public void onStartAsync(AsyncEvent event) throws IOException
+ public void onStartAsync(AsyncEvent event)
{
}
});
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
index 08cfcbaec522..7ebafca10ae6 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
@@ -1880,30 +1880,32 @@ public void testStartAsyncThenClientResetWithoutRemoteErrorNotification(boolean
AtomicReference errorAsyncEventRef = new AtomicReference<>();
AtomicReference responseRef = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
- start(Transport.H2C, new HttpServlet() {
+ start(Transport.H2C, new HttpServlet()
+ {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
{
AsyncContext asyncContext = request.startAsync();
- asyncContext.addListener(new AsyncListener() {
+ asyncContext.addListener(new AsyncListener()
+ {
@Override
- public void onComplete(AsyncEvent event) throws IOException
+ public void onComplete(AsyncEvent event)
{
}
@Override
- public void onTimeout(AsyncEvent event) throws IOException
+ public void onTimeout(AsyncEvent event)
{
}
@Override
- public void onError(AsyncEvent event) throws IOException
+ public void onError(AsyncEvent event)
{
errorAsyncEventRef.set(event);
}
@Override
- public void onStartAsync(AsyncEvent event) throws IOException
+ public void onStartAsync(AsyncEvent event)
{
}
});
From c2d4d2abf381af82670b62d4ffaa6f690cc2ca38 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 11 Jun 2024 17:02:41 +0200
Subject: [PATCH 05/22] rename tests
Signed-off-by: Ludovic Orban
---
.../test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java | 2 +-
.../jetty/ee10/test/client/transport/AsyncIOServletTest.java | 2 +-
.../jetty/ee9/test/client/transport/AsyncIOServletTest.java | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
index 0c45757bdb21..c32a6969fcef 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/AsyncIOTest.java
@@ -254,7 +254,7 @@ public void onClosed(Stream stream)
@ParameterizedTest
@ValueSource(booleans = {true, false})
- public void testClientResetWithoutRemoteErrorNotification(boolean notify) throws Exception
+ public void testClientResetRemoteErrorNotification(boolean notify) throws Exception
{
CountDownLatch latch = new CountDownLatch(1);
AtomicReference responseRef = new AtomicReference<>();
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
index 34562151bae1..db0f3ebef975 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
@@ -1546,7 +1546,7 @@ public void onError(Throwable x)
@ParameterizedTest
@ValueSource(booleans = {true, false})
- public void testStartAsyncThenClientResetWithoutRemoteErrorNotification(boolean notify) throws Exception
+ public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
{
httpConfig.setNotifyRemoteAsyncErrors(notify);
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
index 7ebafca10ae6..11204d699bbc 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
@@ -1873,7 +1873,7 @@ public void onError(Throwable x)
@ParameterizedTest
@ValueSource(booleans = {true, false})
- public void testStartAsyncThenClientResetWithoutRemoteErrorNotification(boolean notify) throws Exception
+ public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
{
httpConfig.setNotifyRemoteAsyncErrors(notify);
From 366328154d2d527496fcb82f26b1034daad13a4e Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Mon, 17 Jun 2024 10:27:08 +0200
Subject: [PATCH 06/22] handle review comments
Signed-off-by: Ludovic Orban
---
.../org/eclipse/jetty/ee10/servlet/ServletChannelState.java | 5 ++---
.../java/org/eclipse/jetty/ee9/nested/HttpChannelState.java | 5 ++---
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
index 46f1fe1050bb..d4c298123ca0 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
@@ -569,11 +569,10 @@ public void startAsync(AsyncContextEvent event)
if (_state != State.HANDLING || (_requestState != RequestState.BLOCKING && _requestState != RequestState.ERRORING))
throw new IllegalStateException(this.getStatusStringLocked());
- if (!_failureListener)
+ if (_servletChannel.getHttpConfiguration().isNotifyRemoteAsyncErrors() && !_failureListener)
{
_failureListener = true;
- if (_servletChannel.getHttpConfiguration().isNotifyRemoteAsyncErrors())
- _servletChannel.getRequest().addFailureListener(this::asyncError);
+ _servletChannel.getRequest().addFailureListener(this::asyncError);
}
_requestState = RequestState.ASYNC;
_event = event;
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
index 24659120a531..cc04f5a5d6a3 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
@@ -531,11 +531,10 @@ public void startAsync(AsyncContextEvent event)
if (_state != State.HANDLING || _requestState != RequestState.BLOCKING)
throw new IllegalStateException(this.getStatusStringLocked());
- if (!_failureListener)
+ if (_channel.getHttpConfiguration().isNotifyRemoteAsyncErrors() && !_failureListener)
{
_failureListener = true;
- if (_channel.getHttpConfiguration().isNotifyRemoteAsyncErrors())
- getHttpChannel().getCoreRequest().addFailureListener(this::asyncError);
+ getHttpChannel().getCoreRequest().addFailureListener(this::asyncError);
}
_requestState = RequestState.ASYNC;
_event = event;
From 8b35d6d7e15b8c7df7ed57b1d0d0b3ebb6c17741 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Mon, 17 Jun 2024 11:21:59 +0200
Subject: [PATCH 07/22] move notifyRemoteAsyncErrors logic to Core API
Signed-off-by: Ludovic Orban
---
.../org/eclipse/jetty/http2/HTTP2Channel.java | 2 +-
.../server/HTTP2ServerConnectionFactory.java | 9 ++-------
.../server/internal/HTTP2ServerConnection.java | 6 +++---
.../server/internal/HttpStreamOverHTTP2.java | 4 ++--
.../internal/ServerHTTP2StreamEndPoint.java | 4 ++--
.../org/eclipse/jetty/server/HttpChannel.java | 15 +++++++++++++--
.../jetty/server/internal/HttpChannelState.java | 15 ++++++++++++++-
.../jetty/ee10/servlet/ServletChannelState.java | 2 +-
.../jetty/ee9/nested/HttpChannelState.java | 2 +-
9 files changed, 39 insertions(+), 20 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java
index ff4ef40c12e2..ef1ab7ea848f 100644
--- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java
+++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java
@@ -63,7 +63,7 @@ public interface Server
// TODO: can it be simplified? The callback seems to only be succeeded, which
// means it can be converted into a Runnable which may just be the return type
// so we can get rid of the Callback parameter.
- public Runnable onFailure(Throwable failure, Callback callback);
+ public Runnable onFailure(Throwable failure, boolean remote, Callback callback);
// TODO: is this needed?
public boolean isIdle();
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
index ba2b27b520b4..fab196ee7e3a 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
@@ -156,7 +156,7 @@ public void onDataAvailable(Stream stream)
public void onReset(Stream stream, ResetFrame frame, Callback callback)
{
EofException failure = new EofException("Reset " + ErrorCode.toString(frame.getError(), null));
- onFailure(stream, failure, callback);
+ getConnection().onStreamFailure(stream, failure, true, callback);
}
@Override
@@ -164,12 +164,7 @@ public void onFailure(Stream stream, int error, String reason, Throwable failure
{
if (!(failure instanceof QuietException))
failure = new EofException(failure);
- onFailure(stream, failure, callback);
- }
-
- private void onFailure(Stream stream, Throwable failure, Callback callback)
- {
- getConnection().onStreamFailure(stream, failure, callback);
+ getConnection().onStreamFailure(stream, failure, false, callback);
}
@Override
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
index 7a2a9299636b..ca0a237fd138 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
@@ -194,14 +194,14 @@ public void onStreamTimeout(Stream stream, TimeoutException timeout, Promise co
}
@Override
- public Runnable onFailure(Throwable failure, Callback callback)
+ public Runnable onFailure(Throwable failure, boolean remote, Callback callback)
{
- Runnable runnable = _httpChannel.onFailure(failure);
+ Runnable runnable = remote ? _httpChannel.onRemoteFailure(failure) : _httpChannel.onFailure(failure);
return () ->
{
if (runnable != null)
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java
index 8f285557f288..30f34ef43dc5 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java
@@ -67,10 +67,10 @@ public void onTimeout(TimeoutException timeout, BiConsumer co
}
@Override
- public Runnable onFailure(Throwable failure, Callback callback)
+ public Runnable onFailure(Throwable failure, boolean remote, Callback callback)
{
if (LOG.isDebugEnabled())
- LOG.debug("failure on {}", this, failure);
+ LOG.debug("{}failure on {}", remote ? "remote " : "", this, failure);
processFailure(failure);
close(failure);
return callback::succeeded;
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index a565ccce5b59..61a157ebd9e8 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -88,8 +88,7 @@ public interface HttpChannel extends Invocable
/**
* Notifies this {@code HttpChannel} that an asynchronous failure happened.
- * Typical failure examples could be HTTP/2 resets or
- * protocol failures (for example, invalid request bytes).
+ * Typical failure examples could be protocol failures (for example, invalid request bytes).
*
* @param failure the failure cause.
* @return a {@code Runnable} that performs the failure action, or {@code null}
@@ -98,6 +97,18 @@ public interface HttpChannel extends Invocable
*/
Runnable onFailure(Throwable failure);
+ /**
+ * Notifies this {@code HttpChannel} that an asynchronous notification was received indicating
+ * a remote failure happened.
+ * Typical failure examples could be HTTP/2 resets.
+ *
+ * @param failure the failure cause.
+ * @return a {@code Runnable} that performs the failure action, or {@code null}
+ * if no failure action needs be performed by the calling thread
+ * @see Request#addFailureListener(Consumer)
+ */
+ Runnable onRemoteFailure(Throwable failure);
+
/**
* Notifies this {@code HttpChannel} that an asynchronous close happened.
*
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
index 7d7ab8cc380b..e97331b573ae 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
@@ -392,6 +392,17 @@ public Runnable onIdleTimeout(TimeoutException t)
@Override
public Runnable onFailure(Throwable x)
+ {
+ return onFailure(x, false);
+ }
+
+ @Override
+ public Runnable onRemoteFailure(Throwable x)
+ {
+ return onFailure(x, true);
+ }
+
+ private Runnable onFailure(Throwable x, boolean remote)
{
HttpStream stream;
Runnable task;
@@ -437,7 +448,9 @@ public Runnable onFailure(Throwable x)
// Notify the failure listeners only once.
Consumer onFailure = _onFailure;
_onFailure = null;
- Runnable invokeOnFailureListeners = onFailure == null || !getHttpConfiguration().isNotifyRemoteAsyncErrors() ? null : () ->
+
+ boolean skipListeners = remote && !getHttpConfiguration().isNotifyRemoteAsyncErrors();
+ Runnable invokeOnFailureListeners = onFailure == null || skipListeners ? null : () ->
{
try
{
diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
index d4c298123ca0..0c419cba5598 100644
--- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
+++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannelState.java
@@ -569,7 +569,7 @@ public void startAsync(AsyncContextEvent event)
if (_state != State.HANDLING || (_requestState != RequestState.BLOCKING && _requestState != RequestState.ERRORING))
throw new IllegalStateException(this.getStatusStringLocked());
- if (_servletChannel.getHttpConfiguration().isNotifyRemoteAsyncErrors() && !_failureListener)
+ if (!_failureListener)
{
_failureListener = true;
_servletChannel.getRequest().addFailureListener(this::asyncError);
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
index cc04f5a5d6a3..7fdc4184b068 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpChannelState.java
@@ -531,7 +531,7 @@ public void startAsync(AsyncContextEvent event)
if (_state != State.HANDLING || _requestState != RequestState.BLOCKING)
throw new IllegalStateException(this.getStatusStringLocked());
- if (_channel.getHttpConfiguration().isNotifyRemoteAsyncErrors() && !_failureListener)
+ if (!_failureListener)
{
_failureListener = true;
getHttpChannel().getCoreRequest().addFailureListener(this::asyncError);
From d375c8afa00b54b8fe9dacae01f799c321d34990 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 10:44:41 +0200
Subject: [PATCH 08/22] wire h3 reset
Signed-off-by: Ludovic Orban
---
.../transport/internal/HttpReceiverOverHTTP3.java | 2 +-
.../eclipse/jetty/http3/client/HTTP3StreamClient.java | 4 ++--
.../java/org/eclipse/jetty/http3/HTTP3Session.java | 6 +++---
.../main/java/org/eclipse/jetty/http3/HTTP3Stream.java | 6 +++---
.../org/eclipse/jetty/http3/HTTP3StreamConnection.java | 10 ++++++++--
.../main/java/org/eclipse/jetty/http3/api/Stream.java | 6 ++++--
.../org/eclipse/jetty/http3/parser/BodyParser.java | 4 ++--
.../eclipse/jetty/http3/parser/HeadersBodyParser.java | 2 +-
.../org/eclipse/jetty/http3/parser/ParserListener.java | 6 +++---
.../http3/server/HTTP3ServerConnectionFactory.java | 6 +++---
.../jetty/http3/server/internal/HTTP3StreamServer.java | 4 ++--
.../http3/server/internal/HttpStreamOverHTTP3.java | 4 ++--
.../server/internal/ServerHTTP3StreamConnection.java | 4 ++--
.../java/org/eclipse/jetty/http3/tests/GoAwayTest.java | 8 ++++----
.../jetty/http3/tests/StreamIdleTimeoutTest.java | 4 ++--
.../quic/quiche/foreign/ForeignQuicheConnection.java | 3 +++
.../jetty/quic/quiche/jna/JnaQuicheConnection.java | 3 +++
17 files changed, 48 insertions(+), 34 deletions(-)
diff --git a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java
index 594de886793c..bf91417bb900 100644
--- a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java
+++ b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java
@@ -154,7 +154,7 @@ public void onIdleTimeout(Stream.Client stream, Throwable failure, Promise prom
}
@Override
- protected void notifyFailure(long error, Throwable failure)
+ protected void notifyFailure(boolean remote, long error, Throwable failure)
{
Listener listener = getListener();
try
{
if (listener != null)
- listener.onFailure(this, error, failure);
+ listener.onFailure(this, remote, error, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
index c6b1b9ce506a..577133e2bc84 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
@@ -682,7 +682,7 @@ private void failStreams(Predicate predicate, long error, String re
stream.reset(error, failure);
// Since the stream failure was generated
// by a GOAWAY, notify the application.
- stream.onFailure(error, failure);
+ stream.onFailure(false, error, failure);
});
}
@@ -814,13 +814,13 @@ private void notifyDisconnect(long error, String reason)
}
@Override
- public void onStreamFailure(long streamId, long error, Throwable failure)
+ public void onStreamFailure(long streamId, boolean remote, long error, Throwable failure)
{
if (LOG.isDebugEnabled())
LOG.debug("stream failure 0x{}/{} for stream #{} on {}", Long.toHexString(error), failure.getMessage(), streamId, this);
HTTP3Stream stream = getStream(streamId);
if (stream != null)
- stream.onFailure(error, failure);
+ stream.onFailure(remote, error, failure);
}
@Override
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
index 2eadbe900122..aea014e7b6f7 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
@@ -370,13 +370,13 @@ public void onTrailer(HeadersFrame frame)
protected abstract void notifyIdleTimeout(TimeoutException timeout, Promise promise);
- public void onFailure(long error, Throwable failure)
+ public void onFailure(boolean remote, long error, Throwable failure)
{
- notifyFailure(error, failure);
+ notifyFailure(remote, error, failure);
session.removeStream(this, failure);
}
- protected abstract void notifyFailure(long error, Throwable failure);
+ protected abstract void notifyFailure(boolean remote, long error, Throwable failure);
protected boolean validateAndUpdate(EnumSet allowed, FrameState target)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
index 29542da3d1eb..699e5eaf1c63 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
@@ -13,6 +13,7 @@
package org.eclipse.jetty.http3;
+import java.io.EOFException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
@@ -150,10 +151,15 @@ private void processDataFrames(boolean setFillInterest)
long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code();
getEndPoint().close(error, x);
// Notify the application that a failure happened.
- parser.getListener().onStreamFailure(getEndPoint().getStreamId(), error, x);
+ parser.getListener().onStreamFailure(getEndPoint().getStreamId(), isReset(x), error, x);
}
}
+ private static boolean isReset(Throwable x)
+ {
+ return x.getCause() instanceof EOFException;
+ }
+
private void processNonDataFrames()
{
try
@@ -238,7 +244,7 @@ private void processNonDataFrames()
long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code();
getEndPoint().close(error, x);
// Notify the application that a failure happened.
- parser.getListener().onStreamFailure(getEndPoint().getStreamId(), error, x);
+ parser.getListener().onStreamFailure(getEndPoint().getStreamId(), isReset(x), error, x);
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java
index d2e83791404a..3129c905fd56 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java
@@ -253,10 +253,11 @@ public default void onIdleTimeout(Client stream, Throwable failure, Promise
*
* @param stream the stream
+ * @param remote true if the error comes from a remote notification
* @param error the failure error
* @param failure the cause of the failure
*/
- public default void onFailure(Stream.Client stream, long error, Throwable failure)
+ public default void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
{
}
}
@@ -369,10 +370,11 @@ public default void onIdleTimeout(Server stream, TimeoutException failure, Promi
* the stream has been reset.
*
* @param stream the stream
+ * @param remote true if the error comes from a remote notification
* @param error the failure error
* @param failure the cause of the failure
*/
- public default void onFailure(Stream.Server stream, long error, Throwable failure)
+ public default void onFailure(Server stream, boolean remote, long error, Throwable failure)
{
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
index 9aa050d57936..2921d04906d6 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
@@ -87,11 +87,11 @@ protected void notifySessionFailure(long error, String reason, Throwable failure
}
}
- protected void notifyStreamFailure(long streamId, long error, Throwable failure)
+ protected void notifyStreamFailure(long streamId, boolean remote, long error, Throwable failure)
{
try
{
- listener.onStreamFailure(streamId, error, failure);
+ listener.onStreamFailure(streamId, remote, error, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
index 686c20712ec2..28b9b8950e28 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
@@ -127,7 +127,7 @@ private boolean decode(ByteBuffer encoded, boolean last)
{
if (LOG.isDebugEnabled())
LOG.debug("decode failure", x);
- notifyStreamFailure(streamId, x.getErrorCode(), x);
+ notifyStreamFailure(streamId, false, x.getErrorCode(), x);
}
catch (QpackException.SessionException x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
index 2db2e77a1357..93048c9624a5 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
@@ -36,7 +36,7 @@ public default void onGoAway(GoAwayFrame frame)
{
}
- public default void onStreamFailure(long streamId, long error, Throwable failure)
+ public default void onStreamFailure(long streamId, boolean remote, long error, Throwable failure)
{
}
@@ -72,9 +72,9 @@ public void onSettings(SettingsFrame frame)
}
@Override
- public void onStreamFailure(long streamId, long error, Throwable failure)
+ public void onStreamFailure(long streamId, boolean remote, long error, Throwable failure)
{
- listener.onStreamFailure(streamId, error, failure);
+ listener.onStreamFailure(streamId, remote, error, failure);
}
@Override
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
index fde8bb5dbfbe..75889de805d9 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
@@ -96,7 +96,7 @@ public void onFailure(Session session, long error, String reason, Throwable fail
{
session.getStreams().stream()
.map(stream -> (HTTP3Stream)stream)
- .forEach(stream -> stream.onFailure(error, failure));
+ .forEach(stream -> stream.onFailure(false, error, failure));
}
}
@@ -165,10 +165,10 @@ public void onIdleTimeout(Stream.Server stream, TimeoutException timeout, Promis
}
@Override
- public void onFailure(Stream.Server stream, long error, Throwable failure)
+ public void onFailure(Stream.Server stream, boolean remote, long error, Throwable failure)
{
HTTP3Stream http3Stream = (HTTP3Stream)stream;
- Runnable task = getConnection().onFailure((HTTP3Stream)stream, failure);
+ Runnable task = getConnection().onFailure((HTTP3Stream)stream, remote, failure);
if (task != null)
{
ServerHTTP3Session protocolSession = (ServerHTTP3Session)http3Stream.getSession().getProtocolSession();
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java
index 9b994a93fce8..2f3a9702638a 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java
@@ -120,13 +120,13 @@ protected void notifyIdleTimeout(TimeoutException timeout, Promise prom
}
@Override
- protected void notifyFailure(long error, Throwable failure)
+ protected void notifyFailure(boolean remote, long error, Throwable failure)
{
Listener listener = this.listener;
try
{
if (listener != null)
- listener.onFailure(this, error, failure);
+ listener.onFailure(this, remote, error, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
index 1552659ad97d..d32cda87da5d 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
@@ -527,7 +527,7 @@ public void onIdleTimeout(TimeoutException failure, BiConsumer true, error, reason, false, failure);
if (notifyFailure)
- onSessionFailure(error, reason, failure);
+ onSessionFailure(error, false, reason, failure);
notifyDisconnect(error, reason);
}
@@ -824,17 +824,17 @@ public void onStreamFailure(long streamId, boolean remote, long error, Throwable
}
@Override
- public void onSessionFailure(long error, String reason, Throwable failure)
+ public void onSessionFailure(long error, boolean remote, String reason, Throwable failure)
{
- notifyFailure(error, reason, failure);
+ notifyFailure(error, remote, reason, failure);
inwardClose(error, reason);
}
- private void notifyFailure(long error, String reason, Throwable failure)
+ private void notifyFailure(long error, boolean remote, String reason, Throwable failure)
{
try
{
- listener.onFailure(this, error, reason, failure);
+ listener.onFailure(this, remote, error, reason, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
index aea014e7b6f7..d2275927788b 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
@@ -392,7 +392,7 @@ protected boolean validateAndUpdate(EnumSet allowed, FrameState targ
if (frameState == FrameState.FAILED)
return false;
frameState = FrameState.FAILED;
- session.onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
+ session.onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), false, "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
return false;
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java
index 22f1a4e2d20d..5f0a74ba1776 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java
@@ -109,28 +109,28 @@ else if (filled < 0)
}
catch (QpackException.SessionException x)
{
- fail(x.getErrorCode(), x.getMessage(), x);
+ fail(x.getErrorCode(), false, x.getMessage(), x);
}
catch (Throwable x)
{
- fail(HTTP3ErrorCode.INTERNAL_ERROR.code(), "internal_error", x);
+ fail(HTTP3ErrorCode.INTERNAL_ERROR.code(), false, "internal_error", x);
}
}
- private void fail(long errorCode, String message, Throwable failure)
+ private void fail(long errorCode, boolean remote, String message, Throwable failure)
{
buffer.release();
buffer = null;
if (LOG.isDebugEnabled())
LOG.debug("could not process instruction stream {}", getEndPoint(), failure);
- notifySessionFailure(errorCode, message, failure);
+ notifySessionFailure(errorCode, remote, message, failure);
}
- protected void notifySessionFailure(long error, String reason, Throwable failure)
+ protected void notifySessionFailure(long error, boolean remote, String reason, Throwable failure)
{
try
{
- listener.onSessionFailure(error, reason, failure);
+ listener.onSessionFailure(error, remote, reason, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java
index 1dccc54ba10f..015a22457c47 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java
@@ -243,7 +243,7 @@ public default void onDisconnect(Session session, long error, String reason)
* @param reason the failure reason
* @param failure the failure
*/
- public default void onFailure(Session session, long error, String reason, Throwable failure)
+ public default void onFailure(Session session, boolean remote, long error, String reason, Throwable failure)
{
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
index 2921d04906d6..f8941b2c39cd 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
@@ -66,20 +66,20 @@ protected long getBodyLength()
protected void emptyBody(ByteBuffer buffer)
{
- sessionFailure(buffer, HTTP3ErrorCode.PROTOCOL_ERROR.code(), "invalid_frame", new IOException("invalid empty body frame"));
+ sessionFailure(buffer, HTTP3ErrorCode.PROTOCOL_ERROR.code(), false, "invalid_frame", new IOException("invalid empty body frame"));
}
- protected void sessionFailure(ByteBuffer buffer, long error, String reason, Throwable failure)
+ protected void sessionFailure(ByteBuffer buffer, long error, boolean remote, String reason, Throwable failure)
{
BufferUtil.clear(buffer);
- notifySessionFailure(error, reason, failure);
+ notifySessionFailure(error, remote, reason, failure);
}
- protected void notifySessionFailure(long error, String reason, Throwable failure)
+ protected void notifySessionFailure(long error, boolean remote, String reason, Throwable failure)
{
try
{
- listener.onSessionFailure(error, reason, failure);
+ listener.onSessionFailure(error, remote, reason, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java
index 9ad1873fc29a..96f3d2091d09 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java
@@ -88,7 +88,7 @@ public void parse(ByteBuffer buffer)
// SPEC: message frames on the control stream are invalid.
if (LOG.isDebugEnabled())
LOG.debug("invalid message frame type {} on control stream", Long.toHexString(frameType));
- sessionFailure(buffer, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid message frame on control stream"));
+ sessionFailure(buffer, false, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid message frame on control stream"));
return;
}
@@ -135,13 +135,13 @@ public void parse(ByteBuffer buffer)
{
if (LOG.isDebugEnabled())
LOG.debug("parse failed", x);
- sessionFailure(buffer, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
+ sessionFailure(buffer, false, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
}
}
- private void sessionFailure(ByteBuffer buffer, long error, String reason, Throwable failure)
+ private void sessionFailure(ByteBuffer buffer, boolean remote, long error, String reason, Throwable failure)
{
- unknownBodyParser.sessionFailure(buffer, error, reason, failure);
+ unknownBodyParser.sessionFailure(buffer, error, remote, reason, failure);
}
private enum State
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
index 28b9b8950e28..7d73a934ccab 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
@@ -133,13 +133,13 @@ private boolean decode(ByteBuffer encoded, boolean last)
{
if (LOG.isDebugEnabled())
LOG.debug("decode failure", x);
- notifySessionFailure(x.getErrorCode(), x.getMessage(), x);
+ notifySessionFailure(x.getErrorCode(), false, x.getMessage(), x);
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("decode failure", x);
- notifySessionFailure(HTTP3ErrorCode.INTERNAL_ERROR.code(), "internal_error", x);
+ notifySessionFailure(HTTP3ErrorCode.INTERNAL_ERROR.code(), false, "internal_error", x);
}
return false;
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
index 2c364a187e25..3d144696b919 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
@@ -147,7 +147,7 @@ public Result parse(ByteBuffer buffer)
// SPEC: control frames on a message stream are invalid.
if (LOG.isDebugEnabled())
LOG.debug("invalid control frame type {} on message stream", Long.toHexString(frameType));
- sessionFailure(buffer, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid control frame in message stream"));
+ sessionFailure(buffer, false, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(),"invalid_frame_type", new IOException("invalid control frame in message stream"));
return Result.NO_FRAME;
}
@@ -207,14 +207,14 @@ public Result parse(ByteBuffer buffer)
{
if (LOG.isDebugEnabled())
LOG.debug("parse failed", x);
- sessionFailure(buffer, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
+ sessionFailure(buffer, false, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
return Result.NO_FRAME;
}
}
- private void sessionFailure(ByteBuffer buffer, long error, String reason, Throwable failure)
+ private void sessionFailure(ByteBuffer buffer, boolean remote, long error, String reason, Throwable failure)
{
- unknownBodyParser.sessionFailure(buffer, error, reason, failure);
+ unknownBodyParser.sessionFailure(buffer, error, remote, reason, failure);
}
public enum Result
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
index 93048c9624a5..d15ac9204d83 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
@@ -40,7 +40,7 @@ public default void onStreamFailure(long streamId, boolean remote, long error, T
{
}
- public default void onSessionFailure(long error, String reason, Throwable failure)
+ public default void onSessionFailure(long error, boolean remote, String reason, Throwable failure)
{
}
@@ -78,9 +78,9 @@ public void onStreamFailure(long streamId, boolean remote, long error, Throwable
}
@Override
- public void onSessionFailure(long error, String reason, Throwable failure)
+ public void onSessionFailure(long error, boolean remote, String reason, Throwable failure)
{
- listener.onSessionFailure(error, reason, failure);
+ listener.onSessionFailure(error, remote, reason, failure);
}
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java
index 8ca5569c8b11..fb467bb6d24a 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java
@@ -73,12 +73,12 @@ public Result parse(ByteBuffer buffer)
{
if (settings.containsKey(key))
{
- sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), "settings_duplicate", new IllegalArgumentException("invalid duplicate setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), false, "settings_duplicate", new IllegalArgumentException("invalid duplicate setting"));
return Result.NO_FRAME;
}
if (SettingsFrame.isReserved(key))
{
- sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), "settings_reserved", new IllegalArgumentException("invalid reserved setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), false, "settings_reserved", new IllegalArgumentException("invalid reserved setting"));
return Result.NO_FRAME;
}
if (length > 0)
@@ -87,7 +87,7 @@ public Result parse(ByteBuffer buffer)
}
else
{
- sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), "settings_invalid_format", new IllegalArgumentException("invalid setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), false, "settings_invalid_format", new IllegalArgumentException("invalid setting"));
return Result.NO_FRAME;
}
break;
@@ -116,7 +116,7 @@ else if (length == 0)
}
else
{
- sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), "settings_invalid_format", new IllegalArgumentException("invalid setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), false, "settings_invalid_format", new IllegalArgumentException("invalid setting"));
return Result.NO_FRAME;
}
break;
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
index 75889de805d9..8c624b173b29 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
@@ -92,11 +92,11 @@ public boolean onIdleTimeout(Session session)
}
@Override
- public void onFailure(Session session, long error, String reason, Throwable failure)
+ public void onFailure(Session session, boolean remote, long error, String reason, Throwable failure)
{
session.getStreams().stream()
.map(stream -> (HTTP3Stream)stream)
- .forEach(stream -> stream.onFailure(false, error, failure));
+ .forEach(stream -> stream.onFailure(remote, error, failure));
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java
index 0bf47b12e260..8351191ebab6 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java
@@ -203,7 +203,7 @@ else if (key == SettingsFrame.MAX_BLOCKED_STREAMS)
private void failControlStream(Throwable failure)
{
long error = HTTP3ErrorCode.CLOSED_CRITICAL_STREAM_ERROR.code();
- onFailure(error, "control_stream_failure", failure);
+ onFailure(false, error, "control_stream_failure", failure);
}
private QuicStreamEndPoint openInstructionEndPoint(long streamId)
@@ -246,9 +246,9 @@ protected boolean onIdleTimeout()
}
@Override
- protected void onFailure(long error, String reason, Throwable failure)
+ protected void onFailure(boolean remote, long error, String reason, Throwable failure)
{
- session.onSessionFailure(HTTP3ErrorCode.NO_ERROR.code(), "failure", failure);
+ session.onSessionFailure(HTTP3ErrorCode.NO_ERROR.code(), remote, "failure", failure);
}
@Override
diff --git a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/UnexpectedFrameTest.java b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/UnexpectedFrameTest.java
index cf69cc3fca84..cd337c43b811 100644
--- a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/UnexpectedFrameTest.java
+++ b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/UnexpectedFrameTest.java
@@ -38,7 +38,7 @@ public void testDataBeforeHeaders() throws Exception
start(new Session.Server.Listener()
{
@Override
- public void onFailure(Session session, long error, String reason, Throwable failure)
+ public void onFailure(Session session, boolean remote, long error, String reason, Throwable failure)
{
assertEquals(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), error);
serverFailureLatch.countDown();
diff --git a/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientProtocolSession.java b/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientProtocolSession.java
index 56760671a9f1..2278fec78f26 100644
--- a/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientProtocolSession.java
+++ b/jetty-core/jetty-quic/jetty-quic-client/src/main/java/org/eclipse/jetty/quic/client/ClientProtocolSession.java
@@ -93,7 +93,7 @@ protected boolean onReadable(long readableStreamId)
}
@Override
- protected void onFailure(long error, String reason, Throwable failure)
+ protected void onFailure(boolean remote, long error, String reason, Throwable failure)
{
outwardClose(QuicErrorCode.NO_ERROR.code(), "failure");
}
diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/ProtocolSession.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/ProtocolSession.java
index 452fdcbacc02..e99d53600dc8 100644
--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/ProtocolSession.java
+++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/ProtocolSession.java
@@ -146,7 +146,7 @@ protected boolean onIdleTimeout()
return true;
}
- protected void onFailure(long error, String reason, Throwable failure)
+ protected void onFailure(boolean remote, long error, String reason, Throwable failure)
{
}
diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java
index c5ff003db1b0..d7dbc2c6cc67 100644
--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java
+++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicConnection.java
@@ -323,7 +323,7 @@ protected Runnable process(QuicSession session, SocketAddress remoteAddress, Byt
{
if (LOG.isDebugEnabled())
LOG.debug("process failure for {}", session, x);
- session.onFailure(x);
+ session.onFailure(false, x);
return null;
}
}
diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java
index 8f998fa8743b..5ec38ffd2df5 100644
--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java
+++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java
@@ -208,9 +208,9 @@ public boolean onIdleTimeout()
return protocolSession.onIdleTimeout();
}
- public void onFailure(Throwable failure)
+ public void onFailure(boolean remote, Throwable failure)
{
- protocolSession.onFailure(QuicErrorCode.NO_ERROR.code(), "failure", failure);
+ protocolSession.onFailure(remote, QuicErrorCode.NO_ERROR.code(), "failure", failure);
}
/**
diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
index b8a2a731a63e..ba35b851f2d9 100644
--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
+++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
@@ -13,6 +13,7 @@
package org.eclipse.jetty.quic.common;
+import java.io.EOFException;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
@@ -268,9 +269,20 @@ public boolean onReadable()
QuicStreamEndPoint streamEndPoint = getQuicSession().getStreamEndPoint(streamId);
if (streamEndPoint.isStreamFinished())
{
+ // Check if the stream was finished because of a reset frame.
+ boolean reset = false;
+ try
+ {
+ streamEndPoint.fill(BufferUtil.EMPTY_BUFFER);
+ }
+ catch (IOException x)
+ {
+ reset = x instanceof EOFException;
+ }
+
EofException e = new EofException();
streamEndPoint.getFillInterest().onFail(e);
- streamEndPoint.getQuicSession().onFailure(e);
+ streamEndPoint.getQuicSession().onFailure(reset, e);
}
}
return interested;
diff --git a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/src/main/java/org/eclipse/jetty/quic/quiche/foreign/ForeignQuicheConnection.java b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/src/main/java/org/eclipse/jetty/quic/quiche/foreign/ForeignQuicheConnection.java
index 78b6da1d6a1d..9aa018d0efe4 100644
--- a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/src/main/java/org/eclipse/jetty/quic/quiche/foreign/ForeignQuicheConnection.java
+++ b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/src/main/java/org/eclipse/jetty/quic/quiche/foreign/ForeignQuicheConnection.java
@@ -919,9 +919,12 @@ public int drainClearBytesForStream(long streamId, ByteBuffer buffer) throws IOE
MemorySegment fin = scope.allocate(NativeHelper.C_CHAR);
read = quiche_h.quiche_conn_stream_recv(quicheConn, streamId, bufferSegment, buffer.remaining(), fin);
- int prevPosition = buffer.position();
- buffer.put(bufferSegment.asByteBuffer().limit((int)read));
- buffer.position(prevPosition);
+ if (read > 0)
+ {
+ int prevPosition = buffer.position();
+ buffer.put(bufferSegment.asByteBuffer().limit((int)read));
+ buffer.position(prevPosition);
+ }
}
}
diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerProtocolSession.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerProtocolSession.java
index 7bca0aa0fcac..fdd07eb6bb91 100644
--- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerProtocolSession.java
+++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerProtocolSession.java
@@ -86,7 +86,7 @@ protected boolean onReadable(long readableStreamId)
}
@Override
- protected void onFailure(long error, String reason, Throwable failure)
+ protected void onFailure(boolean remote, long error, String reason, Throwable failure)
{
// TODO: should probably reset the stream if it exists.
}
From ceaa74602eeadd31c741bfe36f251eebb5019c39 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 11:54:17 +0200
Subject: [PATCH 10/22] add h3 test
Signed-off-by: Ludovic Orban
---
.../test/client/transport/AbstractTest.java | 102 +++++++++++++++++-
.../client/transport/AsyncIOServletTest.java | 34 +++---
2 files changed, 114 insertions(+), 22 deletions(-)
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
index 25cc1d37d96c..104005b44619 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
@@ -21,7 +21,10 @@
import java.security.KeyStore;
import java.util.Collection;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -39,13 +42,16 @@
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Cipher;
-import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.http3.HTTP3ErrorCode;
import org.eclipse.jetty.http3.client.HTTP3Client;
import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3;
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
@@ -66,6 +72,7 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.component.LifeCycle;
@@ -88,7 +95,11 @@ public class AbstractTest
protected AbstractConnector connector;
protected ServletContextHandler servletContextHandler;
protected HttpClient client;
- protected HTTP2Client http2Client;
+
+ private HTTP2Client http2Client;
+ private final Map h2Streams = new HashMap<>();
+ private HTTP3Client http3Client;
+ private final Map h3Streams = new HashMap<>();
public static Collection transports()
{
@@ -120,6 +131,14 @@ public static Collection transportsSecure()
return transports;
}
+ public static Collection transportsWithStreams()
+ {
+ EnumSet transports = EnumSet.of(Transport.H2C, Transport.H3);
+ if ("ci".equals(System.getProperty("env")))
+ transports.remove(Transport.H3);
+ return transports;
+ }
+
@BeforeEach
public void prepare()
{
@@ -129,6 +148,8 @@ public void prepare()
@AfterEach
public void dispose()
{
+ h2Streams.clear();
+ h3Streams.clear();
LifeCycle.stop(client);
LifeCycle.stop(server);
}
@@ -207,16 +228,87 @@ protected void startClient(Transport transport, Consumer consumer) t
client.start();
}
- protected Session newHttp2ClientSession(Session.Listener listener) throws Exception
+ protected long newRequestOnStream(Transport transport) throws Exception
+ {
+ switch (transport)
+ {
+ case H2C, H2 ->
+ {
+ return sendHeadersWithNewH2Stream();
+ }
+ case H3 ->
+ {
+ return sendHeadersWithNewH3Stream();
+ }
+ default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
+ }
+ }
+
+ private int sendHeadersWithNewH2Stream() throws Exception
+ {
+ org.eclipse.jetty.http2.api.Session session = newHttp2ClientSession(new org.eclipse.jetty.http2.api.Session.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, false);
+ FuturePromise promise = new FuturePromise<>();
+ session.newStream(frame, promise, null);
+ org.eclipse.jetty.http2.api.Stream stream = promise.get(5, TimeUnit.SECONDS);
+ int streamId = stream.getId();
+ h2Streams.put(streamId, stream);
+ return streamId;
+ }
+
+ private long sendHeadersWithNewH3Stream() throws Exception
+ {
+ org.eclipse.jetty.http3.api.Session.Client session = newHttp3ClientSession(new org.eclipse.jetty.http3.api.Session.Client.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ CompletableFuture cf = session.newRequest(new org.eclipse.jetty.http3.frames.HeadersFrame(metaData, false), null);
+ org.eclipse.jetty.http3.api.Stream stream = cf.get(5, TimeUnit.SECONDS);
+ long streamId = stream.getId();
+ h3Streams.put(streamId, stream);
+ return streamId;
+ }
+
+ protected void resetStream(Transport transport, long streamId)
+ {
+ switch (transport)
+ {
+ case H2C, H2 -> resetH2Stream((int)streamId);
+ case H3 -> resetH3Stream(streamId);
+ default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
+ }
+ }
+
+ private void resetH2Stream(int streamId)
+ {
+ org.eclipse.jetty.http2.api.Stream stream = h2Streams.get(streamId);
+ stream.reset(new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+ }
+
+ private void resetH3Stream(long streamId)
+ {
+ org.eclipse.jetty.http3.api.Stream stream = h3Streams.get(streamId);
+ stream.reset(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), new Exception(getClass().getSimpleName() + " reset"));
+ }
+
+ private org.eclipse.jetty.http2.api.Session newHttp2ClientSession(org.eclipse.jetty.http2.api.Session.Listener listener) throws Exception
{
String host = "localhost";
int port = ((NetworkConnector)connector).getLocalPort();
InetSocketAddress address = new InetSocketAddress(host, port);
- FuturePromise promise = new FuturePromise<>();
+ FuturePromise promise = new FuturePromise<>();
http2Client.connect(address, listener, promise);
return promise.get(5, TimeUnit.SECONDS);
}
+ private org.eclipse.jetty.http3.api.Session.Client newHttp3ClientSession(org.eclipse.jetty.http3.api.Session.Client.Listener listener) throws Exception
+ {
+ String host = "localhost";
+ int port = ((NetworkConnector)connector).getLocalPort();
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ CompletableFuture cf = http3Client.connect(address, listener);
+ return cf.get(5, TimeUnit.SECONDS);
+ }
+
protected MetaData.Request newRequest(String method, HttpFields fields)
{
return newRequest(method, "/", fields);
@@ -313,7 +405,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
SslContextFactory.Client sslContextFactory = newSslContextFactoryClient();
clientConnector.setSslContextFactory(sslContextFactory);
Path clientPemDirectory = Files.createDirectories(pemDir.resolve("client"));
- HTTP3Client http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
+ http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
yield new HttpClientTransportOverHTTP3(http3Client);
}
case FCGI -> new HttpClientTransportOverFCGI(1, "");
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
index db0f3ebef975..aabe1d99071e 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
@@ -56,19 +56,13 @@
import org.eclipse.jetty.client.StringRequestContent;
import org.eclipse.jetty.client.transport.internal.HttpConnectionOverHTTP;
import org.eclipse.jetty.ee10.servlet.HttpOutput;
-import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.api.Session;
-import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.client.transport.internal.HttpConnectionOverHTTP2;
-import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.logging.StacklessLogging;
@@ -84,7 +78,6 @@
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.jupiter.params.provider.ValueSource;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -1545,15 +1538,27 @@ public void onError(Throwable x)
}
@ParameterizedTest
- @ValueSource(booleans = {true, false})
- public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
+ @MethodSource("transportsWithStreams")
+ public void testStartAsyncThenClientResetRemoteErrorDoesNotify(Transport transport) throws Exception
+ {
+ testStartAsyncThenClientResetRemoteErrorNotification(transport, true);
+ }
+
+ @ParameterizedTest
+ @MethodSource("transportsWithStreams")
+ public void testStartAsyncThenClientResetRemoteErrorDoesNotNotify(Transport transport) throws Exception
+ {
+ testStartAsyncThenClientResetRemoteErrorNotification(transport, false);
+ }
+
+ private void testStartAsyncThenClientResetRemoteErrorNotification(Transport transport, boolean notify) throws Exception
{
httpConfig.setNotifyRemoteAsyncErrors(notify);
AtomicReference errorAsyncEventRef = new AtomicReference<>();
AtomicReference responseRef = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
- start(Transport.H2C, new HttpServlet()
+ start(transport, new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
@@ -1588,18 +1593,13 @@ public void onStartAsync(AsyncEvent event)
}
});
- Session session = newHttp2ClientSession(new Session.Listener() {});
- MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
- HeadersFrame frame = new HeadersFrame(metaData, null, true);
- FuturePromise promise = new FuturePromise<>();
- session.newStream(frame, promise, null);
- Stream stream = promise.get(5, TimeUnit.SECONDS);
+ long streamId = newRequestOnStream(transport);
// Wait for the server to be in ASYNC_WAIT.
assertTrue(latch.await(5, TimeUnit.SECONDS));
sleep(500);
- stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+ resetStream(transport, streamId);
if (notify)
// Wait for the reset to be notified to the async context listener.
From 09ea4e9f1c35250d48ca7b481b8fe1170a80b1df Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 14:16:01 +0200
Subject: [PATCH 11/22] add h3 test
Signed-off-by: Ludovic Orban
---
.../main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java
index c69bb3664108..b326486e504e 100644
--- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java
+++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/HttpOutput.java
@@ -740,7 +740,9 @@ public void flush() throws IOException
catch (Throwable t)
{
onWriteComplete(false, t);
- throw t;
+ if (t instanceof IOException)
+ throw t;
+ throw new IOException(t);
}
}
}
From ecd9e98da3412193a3c81edcaef732010a782283 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 14:17:18 +0200
Subject: [PATCH 12/22] fix checkstyle
Signed-off-by: Ludovic Orban
---
.../src/main/java/org/eclipse/jetty/http3/HTTP3Session.java | 2 +-
.../main/java/org/eclipse/jetty/http3/parser/MessageParser.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
index 035d706b6332..ab6239811c5a 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
@@ -439,7 +439,7 @@ public void onData(long streamId, DataFrame frame)
if (stream != null)
stream.onData(frame);
else
- onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), false,"invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
+ onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), false, "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
}
@Override
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
index 3d144696b919..15b2a1c18b87 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
@@ -147,7 +147,7 @@ public Result parse(ByteBuffer buffer)
// SPEC: control frames on a message stream are invalid.
if (LOG.isDebugEnabled())
LOG.debug("invalid control frame type {} on message stream", Long.toHexString(frameType));
- sessionFailure(buffer, false, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(),"invalid_frame_type", new IOException("invalid control frame in message stream"));
+ sessionFailure(buffer, false, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid control frame in message stream"));
return Result.NO_FRAME;
}
From bf492f4f6b3ffcc348d3ee02761f895e705bbf25 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 14:59:04 +0200
Subject: [PATCH 13/22] add ee9 tests
Signed-off-by: Ludovic Orban
---
.../jetty/quic/common/QuicStreamEndPoint.java | 3 +-
.../jetty-ee9-test-client-transports/pom.xml | 2 +-
.../test/client/transport/AbstractTest.java | 103 +++++++++++++++++-
.../client/transport/AsyncIOServletTest.java | 34 +++---
4 files changed, 118 insertions(+), 24 deletions(-)
diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
index ba35b851f2d9..465a2af0c393 100644
--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
+++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
@@ -42,6 +42,7 @@ public class QuicStreamEndPoint extends AbstractEndPoint
{
private static final Logger LOG = LoggerFactory.getLogger(QuicStreamEndPoint.class);
private static final ByteBuffer LAST_FLAG = ByteBuffer.allocate(0);
+ private static final ByteBuffer EMPTY_WRITABLE_BUFFER = ByteBuffer.allocate(0);
private final QuicSession session;
private final long streamId;
@@ -273,7 +274,7 @@ public boolean onReadable()
boolean reset = false;
try
{
- streamEndPoint.fill(BufferUtil.EMPTY_BUFFER);
+ streamEndPoint.fill(EMPTY_WRITABLE_BUFFER);
}
catch (IOException x)
{
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/pom.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/pom.xml
index e98c0af0c962..4a28e6e5dfd9 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/pom.xml
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/pom.xml
@@ -112,7 +112,7 @@
@{argLine}
${jetty.surefire.argLine}
- --enable-native-access org.eclipse.jetty.quic.quiche.foreign
+ --enable-native-access=ALL-UNNAMED
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
index e7e3b81d429e..d3799a0a84de 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
@@ -25,8 +25,11 @@
import java.security.cert.CertificateException;
import java.util.Collection;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServlet;
@@ -43,13 +46,17 @@
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Cipher;
-import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.http3.HTTP3ErrorCode;
+import org.eclipse.jetty.http3.api.Stream;
import org.eclipse.jetty.http3.client.HTTP3Client;
import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3;
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
@@ -70,6 +77,7 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.component.LifeCycle;
@@ -92,7 +100,11 @@ public class AbstractTest
protected AbstractConnector connector;
protected ServletContextHandler servletContextHandler;
protected HttpClient client;
- protected HTTP2Client http2Client;
+
+ private HTTP2Client http2Client;
+ private final Map h2Streams = new HashMap<>();
+ private HTTP3Client http3Client;
+ private final Map h3Streams = new HashMap<>();
public static Collection transports()
{
@@ -116,6 +128,14 @@ public static Collection transportsWithPushSupport()
return transports;
}
+ public static Collection transportsWithStreams()
+ {
+ EnumSet transports = EnumSet.of(Transport.H2C, Transport.H3);
+ if ("ci".equals(System.getProperty("env")))
+ transports.remove(Transport.H3);
+ return transports;
+ }
+
@BeforeEach
public void prepare()
{
@@ -125,6 +145,8 @@ public void prepare()
@AfterEach
public void dispose()
{
+ h2Streams.clear();
+ h3Streams.clear();
LifeCycle.stop(client);
LifeCycle.stop(server);
}
@@ -206,16 +228,87 @@ protected void startClient(Transport transport) throws Exception
client.start();
}
- protected Session newHttp2ClientSession(Session.Listener listener) throws Exception
+ protected long newRequestOnStream(Transport transport) throws Exception
+ {
+ switch (transport)
+ {
+ case H2C, H2 ->
+ {
+ return sendHeadersWithNewH2Stream();
+ }
+ case H3 ->
+ {
+ return sendHeadersWithNewH3Stream();
+ }
+ default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
+ }
+ }
+
+ private int sendHeadersWithNewH2Stream() throws Exception
+ {
+ org.eclipse.jetty.http2.api.Session session = newHttp2ClientSession(new org.eclipse.jetty.http2.api.Session.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, false);
+ FuturePromise promise = new FuturePromise<>();
+ session.newStream(frame, promise, null);
+ org.eclipse.jetty.http2.api.Stream stream = promise.get(5, TimeUnit.SECONDS);
+ int streamId = stream.getId();
+ h2Streams.put(streamId, stream);
+ return streamId;
+ }
+
+ private long sendHeadersWithNewH3Stream() throws Exception
+ {
+ org.eclipse.jetty.http3.api.Session.Client session = newHttp3ClientSession(new org.eclipse.jetty.http3.api.Session.Client.Listener() {});
+ MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
+ CompletableFuture cf = session.newRequest(new org.eclipse.jetty.http3.frames.HeadersFrame(metaData, false), null);
+ org.eclipse.jetty.http3.api.Stream stream = cf.get(5, TimeUnit.SECONDS);
+ long streamId = stream.getId();
+ h3Streams.put(streamId, stream);
+ return streamId;
+ }
+
+ protected void resetStream(Transport transport, long streamId)
+ {
+ switch (transport)
+ {
+ case H2C, H2 -> resetH2Stream((int)streamId);
+ case H3 -> resetH3Stream(streamId);
+ default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
+ }
+ }
+
+ private void resetH2Stream(int streamId)
+ {
+ org.eclipse.jetty.http2.api.Stream stream = h2Streams.get(streamId);
+ stream.reset(new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+ }
+
+ private void resetH3Stream(long streamId)
+ {
+ org.eclipse.jetty.http3.api.Stream stream = h3Streams.get(streamId);
+ stream.reset(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), new Exception(getClass().getSimpleName() + " reset"));
+ }
+
+ private org.eclipse.jetty.http2.api.Session newHttp2ClientSession(org.eclipse.jetty.http2.api.Session.Listener listener) throws Exception
{
String host = "localhost";
int port = ((NetworkConnector)connector).getLocalPort();
InetSocketAddress address = new InetSocketAddress(host, port);
- FuturePromise promise = new FuturePromise<>();
+ FuturePromise promise = new FuturePromise<>();
http2Client.connect(address, listener, promise);
return promise.get(5, TimeUnit.SECONDS);
}
+ private org.eclipse.jetty.http3.api.Session.Client newHttp3ClientSession(org.eclipse.jetty.http3.api.Session.Client.Listener listener) throws Exception
+ {
+ String host = "localhost";
+ int port = ((NetworkConnector)connector).getLocalPort();
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ CompletableFuture cf = http3Client.connect(address, listener);
+ return cf.get(5, TimeUnit.SECONDS);
+ }
+
protected MetaData.Request newRequest(String method, HttpFields fields)
{
return newRequest(method, "/", fields);
@@ -311,7 +404,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
SslContextFactory.Client sslContextFactory = newSslContextFactoryClient();
clientConnector.setSslContextFactory(sslContextFactory);
Path clientPemDirectory = Files.createDirectories(pemDir.resolve("client"));
- HTTP3Client http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
+ http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
yield new HttpClientTransportOverHTTP3(http3Client);
}
case FCGI -> new HttpClientTransportOverFCGI(1, "");
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
index 11204d699bbc..e4a674b6b846 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
@@ -58,19 +58,13 @@
import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.nested.HttpInput;
import org.eclipse.jetty.ee9.nested.HttpOutput;
-import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.api.Session;
-import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.client.transport.internal.HttpConnectionOverHTTP2;
-import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.logging.StacklessLogging;
@@ -84,7 +78,6 @@
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.jupiter.params.provider.ValueSource;
import static java.nio.ByteBuffer.wrap;
import static org.awaitility.Awaitility.await;
@@ -1872,15 +1865,27 @@ public void onError(Throwable x)
}
@ParameterizedTest
- @ValueSource(booleans = {true, false})
- public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
+ @MethodSource("transportsWithStreams")
+ public void testStartAsyncThenClientResetRemoteErrorDoesNotify(Transport transport) throws Exception
+ {
+ testStartAsyncThenClientResetRemoteErrorNotification(transport, true);
+ }
+
+ @ParameterizedTest
+ @MethodSource("transportsWithStreams")
+ public void testStartAsyncThenClientResetRemoteErrorDoesNotNotify(Transport transport) throws Exception
+ {
+ testStartAsyncThenClientResetRemoteErrorNotification(transport, false);
+ }
+
+ private void testStartAsyncThenClientResetRemoteErrorNotification(Transport transport, boolean notify) throws Exception
{
httpConfig.setNotifyRemoteAsyncErrors(notify);
AtomicReference errorAsyncEventRef = new AtomicReference<>();
AtomicReference responseRef = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
- start(Transport.H2C, new HttpServlet()
+ start(transport, new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
@@ -1915,18 +1920,13 @@ public void onStartAsync(AsyncEvent event)
}
});
- Session session = newHttp2ClientSession(new Session.Listener() {});
- MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
- HeadersFrame frame = new HeadersFrame(metaData, null, true);
- FuturePromise promise = new FuturePromise<>();
- session.newStream(frame, promise, null);
- Stream stream = promise.get(5, TimeUnit.SECONDS);
+ long streamId = newRequestOnStream(transport);
// Wait for the server to be in ASYNC_WAIT.
assertTrue(latch.await(5, TimeUnit.SECONDS));
sleep(500);
- stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
+ resetStream(transport, streamId);
if (notify)
// Wait for the reset to be notified to the async context listener.
From 1de023fc59c6b983cd6aeef580f513c305490a61 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 15:03:18 +0200
Subject: [PATCH 14/22] remove unneeded code
Signed-off-by: Ludovic Orban
---
.../org/eclipse/jetty/http3/HTTP3StreamConnection.java | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
index 699e5eaf1c63..bbec04eea7ea 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
@@ -13,7 +13,6 @@
package org.eclipse.jetty.http3;
-import java.io.EOFException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
@@ -151,15 +150,10 @@ private void processDataFrames(boolean setFillInterest)
long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code();
getEndPoint().close(error, x);
// Notify the application that a failure happened.
- parser.getListener().onStreamFailure(getEndPoint().getStreamId(), isReset(x), error, x);
+ parser.getListener().onStreamFailure(getEndPoint().getStreamId(), false, error, x);
}
}
- private static boolean isReset(Throwable x)
- {
- return x.getCause() instanceof EOFException;
- }
-
private void processNonDataFrames()
{
try
@@ -244,7 +238,7 @@ private void processNonDataFrames()
long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code();
getEndPoint().close(error, x);
// Notify the application that a failure happened.
- parser.getListener().onStreamFailure(getEndPoint().getStreamId(), isReset(x), error, x);
+ parser.getListener().onStreamFailure(getEndPoint().getStreamId(), false, error, x);
}
}
From efc71700e0f2e3f51d1c2625a1e24aa7d92cc01f Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 15:07:08 +0200
Subject: [PATCH 15/22] fix compilation
Signed-off-by: Ludovic Orban
---
.../java/org/eclipse/jetty/http3/tests/ClientServerTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java
index 7e0b11832a20..74edd5a5efa2 100644
--- a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java
+++ b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java
@@ -492,7 +492,7 @@ public void onSettings(Session session, SettingsFrame frame)
clientSession.newRequest(new HeadersFrame(newRequest("/large"), true), new Stream.Client.Listener()
{
@Override
- public void onFailure(Stream.Client stream, long error, Throwable failure)
+ public void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
{
streamFailureLatch.countDown();
}
From 9229515f0c5804ab9a4b32dcf32a7ac37fa44e10 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 15:39:57 +0200
Subject: [PATCH 16/22] fix doc
Signed-off-by: Ludovic Orban
---
.../jetty/docs/programming/client/http3/HTTP3ClientDocs.java | 4 ++--
.../jetty/docs/programming/client/http3/HTTP3ClientDocs.java | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
index a18a2b14903c..dcc9ca374544 100644
--- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
@@ -257,9 +257,9 @@ public void reset() throws Exception
CompletableFuture streamCF = session.newRequest(headersFrame, new Stream.Client.Listener()
{
@Override
- public void onFailure(Stream.Client stream, long error, Throwable failure)
+ public void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
{
- // The server reset this stream.
+ // The server reset this stream when remote is true.
}
});
Stream stream = streamCF.get();
diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
index a18a2b14903c..dcc9ca374544 100644
--- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
+++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
@@ -257,9 +257,9 @@ public void reset() throws Exception
CompletableFuture streamCF = session.newRequest(headersFrame, new Stream.Client.Listener()
{
@Override
- public void onFailure(Stream.Client stream, long error, Throwable failure)
+ public void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
{
- // The server reset this stream.
+ // The server reset this stream when remote is true.
}
});
Stream stream = streamCF.get();
From 2a63ee4c9f0b056e538233978b87bceef10dc3e8 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 17:09:57 +0200
Subject: [PATCH 17/22] rewire h3
Signed-off-by: Ludovic Orban
---
.../client/http3/HTTP3ClientDocs.java | 4 ++--
.../client/http3/HTTP3ClientDocs.java | 4 ++--
.../server/internal/HttpStreamOverHTTP2.java | 1 +
.../internal/HttpReceiverOverHTTP3.java | 2 +-
.../internal/SessionClientListener.java | 4 ++--
.../jetty/http3/client/ClientHTTP3Session.java | 6 +++---
.../jetty/http3/client/HTTP3StreamClient.java | 4 ++--
.../org/eclipse/jetty/http3/HTTP3Session.java | 18 +++++++++---------
.../org/eclipse/jetty/http3/HTTP3Stream.java | 8 ++++----
.../jetty/http3/HTTP3StreamConnection.java | 4 ++--
.../http3/InstructionStreamConnection.java | 12 ++++++------
.../org/eclipse/jetty/http3/api/Session.java | 2 +-
.../org/eclipse/jetty/http3/api/Stream.java | 6 ++----
.../eclipse/jetty/http3/parser/BodyParser.java | 14 +++++++-------
.../jetty/http3/parser/ControlParser.java | 8 ++++----
.../jetty/http3/parser/HeadersBodyParser.java | 6 +++---
.../jetty/http3/parser/MessageParser.java | 8 ++++----
.../jetty/http3/parser/ParserListener.java | 12 ++++++------
.../jetty/http3/parser/SettingsBodyParser.java | 8 ++++----
.../server/HTTP3ServerConnectionFactory.java | 8 ++++----
.../server/internal/HTTP3StreamServer.java | 4 ++--
.../server/internal/HttpStreamOverHTTP3.java | 5 ++++-
.../server/internal/ServerHTTP3Session.java | 6 +++---
.../internal/ServerHTTP3StreamConnection.java | 4 ++--
.../jetty/http3/tests/ClientServerTest.java | 2 +-
.../eclipse/jetty/http3/tests/GoAwayTest.java | 8 ++++----
.../http3/tests/StreamIdleTimeoutTest.java | 4 ++--
.../jetty/http3/tests/UnexpectedFrameTest.java | 2 +-
.../quic/client/ClientProtocolSession.java | 2 +-
.../jetty/quic/common/ProtocolSession.java | 2 +-
.../jetty/quic/common/QuicConnection.java | 2 +-
.../eclipse/jetty/quic/common/QuicSession.java | 4 ++--
.../jetty/quic/common/QuicStreamEndPoint.java | 11 +++++------
.../quic/server/ServerProtocolSession.java | 2 +-
34 files changed, 99 insertions(+), 98 deletions(-)
diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
index dcc9ca374544..a18a2b14903c 100644
--- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
@@ -257,9 +257,9 @@ public void reset() throws Exception
CompletableFuture streamCF = session.newRequest(headersFrame, new Stream.Client.Listener()
{
@Override
- public void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
+ public void onFailure(Stream.Client stream, long error, Throwable failure)
{
- // The server reset this stream when remote is true.
+ // The server reset this stream.
}
});
Stream stream = streamCF.get();
diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
index dcc9ca374544..a18a2b14903c 100644
--- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
+++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java
@@ -257,9 +257,9 @@ public void reset() throws Exception
CompletableFuture streamCF = session.newRequest(headersFrame, new Stream.Client.Listener()
{
@Override
- public void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
+ public void onFailure(Stream.Client stream, long error, Throwable failure)
{
- // The server reset this stream when remote is true.
+ // The server reset this stream.
}
});
Stream stream = streamCF.get();
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
index 820b46d01ffc..89b98f9776a4 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
@@ -587,6 +587,7 @@ public void onTimeout(TimeoutException timeout, BiConsumer co
@Override
public Runnable onFailure(Throwable failure, boolean remote, Callback callback)
{
+ // TODO failure.getCause() instanceof EOFException
Runnable runnable = remote ? _httpChannel.onRemoteFailure(failure) : _httpChannel.onFailure(failure);
return () ->
{
diff --git a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java
index bf91417bb900..594de886793c 100644
--- a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java
+++ b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/internal/HttpReceiverOverHTTP3.java
@@ -154,7 +154,7 @@ public void onIdleTimeout(Stream.Client stream, Throwable failure, Promise prom
}
@Override
- protected void notifyFailure(boolean remote, long error, Throwable failure)
+ protected void notifyFailure(long error, Throwable failure)
{
Listener listener = getListener();
try
{
if (listener != null)
- listener.onFailure(this, remote, error, failure);
+ listener.onFailure(this, error, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
index ab6239811c5a..c6b1b9ce506a 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java
@@ -439,7 +439,7 @@ public void onData(long streamId, DataFrame frame)
if (stream != null)
stream.onData(frame);
else
- onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), false, "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
+ onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
}
@Override
@@ -682,7 +682,7 @@ private void failStreams(Predicate predicate, long error, String re
stream.reset(error, failure);
// Since the stream failure was generated
// by a GOAWAY, notify the application.
- stream.onFailure(false, error, failure);
+ stream.onFailure(error, failure);
});
}
@@ -796,7 +796,7 @@ public void onClose(long error, String reason)
failStreams(stream -> true, error, reason, false, failure);
if (notifyFailure)
- onSessionFailure(error, false, reason, failure);
+ onSessionFailure(error, reason, failure);
notifyDisconnect(error, reason);
}
@@ -814,27 +814,27 @@ private void notifyDisconnect(long error, String reason)
}
@Override
- public void onStreamFailure(long streamId, boolean remote, long error, Throwable failure)
+ public void onStreamFailure(long streamId, long error, Throwable failure)
{
if (LOG.isDebugEnabled())
LOG.debug("stream failure 0x{}/{} for stream #{} on {}", Long.toHexString(error), failure.getMessage(), streamId, this);
HTTP3Stream stream = getStream(streamId);
if (stream != null)
- stream.onFailure(remote, error, failure);
+ stream.onFailure(error, failure);
}
@Override
- public void onSessionFailure(long error, boolean remote, String reason, Throwable failure)
+ public void onSessionFailure(long error, String reason, Throwable failure)
{
- notifyFailure(error, remote, reason, failure);
+ notifyFailure(error, reason, failure);
inwardClose(error, reason);
}
- private void notifyFailure(long error, boolean remote, String reason, Throwable failure)
+ private void notifyFailure(long error, String reason, Throwable failure)
{
try
{
- listener.onFailure(this, remote, error, reason, failure);
+ listener.onFailure(this, error, reason, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
index d2275927788b..2eadbe900122 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Stream.java
@@ -370,13 +370,13 @@ public void onTrailer(HeadersFrame frame)
protected abstract void notifyIdleTimeout(TimeoutException timeout, Promise promise);
- public void onFailure(boolean remote, long error, Throwable failure)
+ public void onFailure(long error, Throwable failure)
{
- notifyFailure(remote, error, failure);
+ notifyFailure(error, failure);
session.removeStream(this, failure);
}
- protected abstract void notifyFailure(boolean remote, long error, Throwable failure);
+ protected abstract void notifyFailure(long error, Throwable failure);
protected boolean validateAndUpdate(EnumSet allowed, FrameState target)
{
@@ -392,7 +392,7 @@ protected boolean validateAndUpdate(EnumSet allowed, FrameState targ
if (frameState == FrameState.FAILED)
return false;
frameState = FrameState.FAILED;
- session.onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), false, "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
+ session.onSessionFailure(HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_sequence", new IllegalStateException("invalid frame sequence"));
return false;
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
index bbec04eea7ea..29542da3d1eb 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
@@ -150,7 +150,7 @@ private void processDataFrames(boolean setFillInterest)
long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code();
getEndPoint().close(error, x);
// Notify the application that a failure happened.
- parser.getListener().onStreamFailure(getEndPoint().getStreamId(), false, error, x);
+ parser.getListener().onStreamFailure(getEndPoint().getStreamId(), error, x);
}
}
@@ -238,7 +238,7 @@ private void processNonDataFrames()
long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code();
getEndPoint().close(error, x);
// Notify the application that a failure happened.
- parser.getListener().onStreamFailure(getEndPoint().getStreamId(), false, error, x);
+ parser.getListener().onStreamFailure(getEndPoint().getStreamId(), error, x);
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java
index 5f0a74ba1776..22f1a4e2d20d 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/InstructionStreamConnection.java
@@ -109,28 +109,28 @@ else if (filled < 0)
}
catch (QpackException.SessionException x)
{
- fail(x.getErrorCode(), false, x.getMessage(), x);
+ fail(x.getErrorCode(), x.getMessage(), x);
}
catch (Throwable x)
{
- fail(HTTP3ErrorCode.INTERNAL_ERROR.code(), false, "internal_error", x);
+ fail(HTTP3ErrorCode.INTERNAL_ERROR.code(), "internal_error", x);
}
}
- private void fail(long errorCode, boolean remote, String message, Throwable failure)
+ private void fail(long errorCode, String message, Throwable failure)
{
buffer.release();
buffer = null;
if (LOG.isDebugEnabled())
LOG.debug("could not process instruction stream {}", getEndPoint(), failure);
- notifySessionFailure(errorCode, remote, message, failure);
+ notifySessionFailure(errorCode, message, failure);
}
- protected void notifySessionFailure(long error, boolean remote, String reason, Throwable failure)
+ protected void notifySessionFailure(long error, String reason, Throwable failure)
{
try
{
- listener.onSessionFailure(error, remote, reason, failure);
+ listener.onSessionFailure(error, reason, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java
index 015a22457c47..1dccc54ba10f 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Session.java
@@ -243,7 +243,7 @@ public default void onDisconnect(Session session, long error, String reason)
* @param reason the failure reason
* @param failure the failure
*/
- public default void onFailure(Session session, boolean remote, long error, String reason, Throwable failure)
+ public default void onFailure(Session session, long error, String reason, Throwable failure)
{
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java
index 3129c905fd56..d2e83791404a 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/api/Stream.java
@@ -253,11 +253,10 @@ public default void onIdleTimeout(Client stream, Throwable failure, Promise
*
* @param stream the stream
- * @param remote true if the error comes from a remote notification
* @param error the failure error
* @param failure the cause of the failure
*/
- public default void onFailure(Stream.Client stream, boolean remote, long error, Throwable failure)
+ public default void onFailure(Stream.Client stream, long error, Throwable failure)
{
}
}
@@ -370,11 +369,10 @@ public default void onIdleTimeout(Server stream, TimeoutException failure, Promi
* the stream has been reset.
*
* @param stream the stream
- * @param remote true if the error comes from a remote notification
* @param error the failure error
* @param failure the cause of the failure
*/
- public default void onFailure(Server stream, boolean remote, long error, Throwable failure)
+ public default void onFailure(Stream.Server stream, long error, Throwable failure)
{
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
index f8941b2c39cd..9aa050d57936 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/BodyParser.java
@@ -66,20 +66,20 @@ protected long getBodyLength()
protected void emptyBody(ByteBuffer buffer)
{
- sessionFailure(buffer, HTTP3ErrorCode.PROTOCOL_ERROR.code(), false, "invalid_frame", new IOException("invalid empty body frame"));
+ sessionFailure(buffer, HTTP3ErrorCode.PROTOCOL_ERROR.code(), "invalid_frame", new IOException("invalid empty body frame"));
}
- protected void sessionFailure(ByteBuffer buffer, long error, boolean remote, String reason, Throwable failure)
+ protected void sessionFailure(ByteBuffer buffer, long error, String reason, Throwable failure)
{
BufferUtil.clear(buffer);
- notifySessionFailure(error, remote, reason, failure);
+ notifySessionFailure(error, reason, failure);
}
- protected void notifySessionFailure(long error, boolean remote, String reason, Throwable failure)
+ protected void notifySessionFailure(long error, String reason, Throwable failure)
{
try
{
- listener.onSessionFailure(error, remote, reason, failure);
+ listener.onSessionFailure(error, reason, failure);
}
catch (Throwable x)
{
@@ -87,11 +87,11 @@ protected void notifySessionFailure(long error, boolean remote, String reason, T
}
}
- protected void notifyStreamFailure(long streamId, boolean remote, long error, Throwable failure)
+ protected void notifyStreamFailure(long streamId, long error, Throwable failure)
{
try
{
- listener.onStreamFailure(streamId, remote, error, failure);
+ listener.onStreamFailure(streamId, error, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java
index 96f3d2091d09..9ad1873fc29a 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ControlParser.java
@@ -88,7 +88,7 @@ public void parse(ByteBuffer buffer)
// SPEC: message frames on the control stream are invalid.
if (LOG.isDebugEnabled())
LOG.debug("invalid message frame type {} on control stream", Long.toHexString(frameType));
- sessionFailure(buffer, false, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid message frame on control stream"));
+ sessionFailure(buffer, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid message frame on control stream"));
return;
}
@@ -135,13 +135,13 @@ public void parse(ByteBuffer buffer)
{
if (LOG.isDebugEnabled())
LOG.debug("parse failed", x);
- sessionFailure(buffer, false, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
+ sessionFailure(buffer, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
}
}
- private void sessionFailure(ByteBuffer buffer, boolean remote, long error, String reason, Throwable failure)
+ private void sessionFailure(ByteBuffer buffer, long error, String reason, Throwable failure)
{
- unknownBodyParser.sessionFailure(buffer, error, remote, reason, failure);
+ unknownBodyParser.sessionFailure(buffer, error, reason, failure);
}
private enum State
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
index 7d73a934ccab..686c20712ec2 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/HeadersBodyParser.java
@@ -127,19 +127,19 @@ private boolean decode(ByteBuffer encoded, boolean last)
{
if (LOG.isDebugEnabled())
LOG.debug("decode failure", x);
- notifyStreamFailure(streamId, false, x.getErrorCode(), x);
+ notifyStreamFailure(streamId, x.getErrorCode(), x);
}
catch (QpackException.SessionException x)
{
if (LOG.isDebugEnabled())
LOG.debug("decode failure", x);
- notifySessionFailure(x.getErrorCode(), false, x.getMessage(), x);
+ notifySessionFailure(x.getErrorCode(), x.getMessage(), x);
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("decode failure", x);
- notifySessionFailure(HTTP3ErrorCode.INTERNAL_ERROR.code(), false, "internal_error", x);
+ notifySessionFailure(HTTP3ErrorCode.INTERNAL_ERROR.code(), "internal_error", x);
}
return false;
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
index 15b2a1c18b87..2c364a187e25 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/MessageParser.java
@@ -147,7 +147,7 @@ public Result parse(ByteBuffer buffer)
// SPEC: control frames on a message stream are invalid.
if (LOG.isDebugEnabled())
LOG.debug("invalid control frame type {} on message stream", Long.toHexString(frameType));
- sessionFailure(buffer, false, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid control frame in message stream"));
+ sessionFailure(buffer, HTTP3ErrorCode.FRAME_UNEXPECTED_ERROR.code(), "invalid_frame_type", new IOException("invalid control frame in message stream"));
return Result.NO_FRAME;
}
@@ -207,14 +207,14 @@ public Result parse(ByteBuffer buffer)
{
if (LOG.isDebugEnabled())
LOG.debug("parse failed", x);
- sessionFailure(buffer, false, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
+ sessionFailure(buffer, HTTP3ErrorCode.INTERNAL_ERROR.code(), "parser_error", x);
return Result.NO_FRAME;
}
}
- private void sessionFailure(ByteBuffer buffer, boolean remote, long error, String reason, Throwable failure)
+ private void sessionFailure(ByteBuffer buffer, long error, String reason, Throwable failure)
{
- unknownBodyParser.sessionFailure(buffer, error, remote, reason, failure);
+ unknownBodyParser.sessionFailure(buffer, error, reason, failure);
}
public enum Result
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
index d15ac9204d83..2db2e77a1357 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/ParserListener.java
@@ -36,11 +36,11 @@ public default void onGoAway(GoAwayFrame frame)
{
}
- public default void onStreamFailure(long streamId, boolean remote, long error, Throwable failure)
+ public default void onStreamFailure(long streamId, long error, Throwable failure)
{
}
- public default void onSessionFailure(long error, boolean remote, String reason, Throwable failure)
+ public default void onSessionFailure(long error, String reason, Throwable failure)
{
}
@@ -72,15 +72,15 @@ public void onSettings(SettingsFrame frame)
}
@Override
- public void onStreamFailure(long streamId, boolean remote, long error, Throwable failure)
+ public void onStreamFailure(long streamId, long error, Throwable failure)
{
- listener.onStreamFailure(streamId, remote, error, failure);
+ listener.onStreamFailure(streamId, error, failure);
}
@Override
- public void onSessionFailure(long error, boolean remote, String reason, Throwable failure)
+ public void onSessionFailure(long error, String reason, Throwable failure)
{
- listener.onSessionFailure(error, remote, reason, failure);
+ listener.onSessionFailure(error, reason, failure);
}
}
}
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java
index fb467bb6d24a..8ca5569c8b11 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/parser/SettingsBodyParser.java
@@ -73,12 +73,12 @@ public Result parse(ByteBuffer buffer)
{
if (settings.containsKey(key))
{
- sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), false, "settings_duplicate", new IllegalArgumentException("invalid duplicate setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), "settings_duplicate", new IllegalArgumentException("invalid duplicate setting"));
return Result.NO_FRAME;
}
if (SettingsFrame.isReserved(key))
{
- sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), false, "settings_reserved", new IllegalArgumentException("invalid reserved setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.SETTINGS_ERROR.code(), "settings_reserved", new IllegalArgumentException("invalid reserved setting"));
return Result.NO_FRAME;
}
if (length > 0)
@@ -87,7 +87,7 @@ public Result parse(ByteBuffer buffer)
}
else
{
- sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), false, "settings_invalid_format", new IllegalArgumentException("invalid setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), "settings_invalid_format", new IllegalArgumentException("invalid setting"));
return Result.NO_FRAME;
}
break;
@@ -116,7 +116,7 @@ else if (length == 0)
}
else
{
- sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), false, "settings_invalid_format", new IllegalArgumentException("invalid setting"));
+ sessionFailure(buffer, HTTP3ErrorCode.FRAME_ERROR.code(), "settings_invalid_format", new IllegalArgumentException("invalid setting"));
return Result.NO_FRAME;
}
break;
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
index 8c624b173b29..fde8bb5dbfbe 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/HTTP3ServerConnectionFactory.java
@@ -92,11 +92,11 @@ public boolean onIdleTimeout(Session session)
}
@Override
- public void onFailure(Session session, boolean remote, long error, String reason, Throwable failure)
+ public void onFailure(Session session, long error, String reason, Throwable failure)
{
session.getStreams().stream()
.map(stream -> (HTTP3Stream)stream)
- .forEach(stream -> stream.onFailure(remote, error, failure));
+ .forEach(stream -> stream.onFailure(error, failure));
}
}
@@ -165,10 +165,10 @@ public void onIdleTimeout(Stream.Server stream, TimeoutException timeout, Promis
}
@Override
- public void onFailure(Stream.Server stream, boolean remote, long error, Throwable failure)
+ public void onFailure(Stream.Server stream, long error, Throwable failure)
{
HTTP3Stream http3Stream = (HTTP3Stream)stream;
- Runnable task = getConnection().onFailure((HTTP3Stream)stream, remote, failure);
+ Runnable task = getConnection().onFailure((HTTP3Stream)stream, failure);
if (task != null)
{
ServerHTTP3Session protocolSession = (ServerHTTP3Session)http3Stream.getSession().getProtocolSession();
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java
index 2f3a9702638a..9b994a93fce8 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3StreamServer.java
@@ -120,13 +120,13 @@ protected void notifyIdleTimeout(TimeoutException timeout, Promise prom
}
@Override
- protected void notifyFailure(boolean remote, long error, Throwable failure)
+ protected void notifyFailure(long error, Throwable failure)
{
Listener listener = this.listener;
try
{
if (listener != null)
- listener.onFailure(this, remote, error, failure);
+ listener.onFailure(this, error, failure);
}
catch (Throwable x)
{
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
index d32cda87da5d..5299ac9a9230 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
@@ -13,6 +13,7 @@
package org.eclipse.jetty.http3.server.internal;
+import java.io.EOFException;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
@@ -527,7 +528,7 @@ public void onIdleTimeout(TimeoutException failure, BiConsumer
Date: Tue, 18 Jun 2024 17:17:55 +0200
Subject: [PATCH 18/22] rewire h2
Signed-off-by: Ludovic Orban
---
.../java/org/eclipse/jetty/http2/HTTP2Channel.java | 2 +-
.../http2/server/HTTP2ServerConnectionFactory.java | 11 +++++++++--
.../http2/server/internal/HTTP2ServerConnection.java | 6 +++---
.../http2/server/internal/HttpStreamOverHTTP2.java | 5 +++--
.../server/internal/ServerHTTP2StreamEndPoint.java | 4 ++--
5 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java
index ef1ab7ea848f..ff4ef40c12e2 100644
--- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java
+++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Channel.java
@@ -63,7 +63,7 @@ public interface Server
// TODO: can it be simplified? The callback seems to only be succeeded, which
// means it can be converted into a Runnable which may just be the return type
// so we can get rid of the Callback parameter.
- public Runnable onFailure(Throwable failure, boolean remote, Callback callback);
+ public Runnable onFailure(Throwable failure, Callback callback);
// TODO: is this needed?
public boolean isIdle();
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
index fab196ee7e3a..a254f4970e93 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
@@ -13,6 +13,7 @@
package org.eclipse.jetty.http2.server;
+import java.io.EOFException;
import java.util.Map;
import java.util.concurrent.TimeoutException;
@@ -156,7 +157,8 @@ public void onDataAvailable(Stream stream)
public void onReset(Stream stream, ResetFrame frame, Callback callback)
{
EofException failure = new EofException("Reset " + ErrorCode.toString(frame.getError(), null));
- getConnection().onStreamFailure(stream, failure, true, callback);
+ failure.initCause(new EOFException()); // reset marker
+ onFailure(stream, failure, callback);
}
@Override
@@ -164,7 +166,12 @@ public void onFailure(Stream stream, int error, String reason, Throwable failure
{
if (!(failure instanceof QuietException))
failure = new EofException(failure);
- getConnection().onStreamFailure(stream, failure, false, callback);
+ onFailure(stream, failure, callback);
+ }
+
+ private void onFailure(Stream stream, Throwable failure, Callback callback)
+ {
+ getConnection().onStreamFailure(stream, failure, callback);
}
@Override
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
index ca0a237fd138..7a2a9299636b 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
@@ -194,14 +194,14 @@ public void onStreamTimeout(Stream stream, TimeoutException timeout, Promise co
}
@Override
- public Runnable onFailure(Throwable failure, boolean remote, Callback callback)
+ public Runnable onFailure(Throwable failure, Callback callback)
{
- // TODO failure.getCause() instanceof EOFException
+ boolean remote = failure != null && failure.getCause() instanceof EOFException;
Runnable runnable = remote ? _httpChannel.onRemoteFailure(failure) : _httpChannel.onFailure(failure);
return () ->
{
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java
index 30f34ef43dc5..8f285557f288 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/ServerHTTP2StreamEndPoint.java
@@ -67,10 +67,10 @@ public void onTimeout(TimeoutException timeout, BiConsumer co
}
@Override
- public Runnable onFailure(Throwable failure, boolean remote, Callback callback)
+ public Runnable onFailure(Throwable failure, Callback callback)
{
if (LOG.isDebugEnabled())
- LOG.debug("{}failure on {}", remote ? "remote " : "", this, failure);
+ LOG.debug("failure on {}", this, failure);
processFailure(failure);
close(failure);
return callback::succeeded;
From 3d2e2fdd3faf5e804469ddac28df603effae32cc Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 17:41:15 +0200
Subject: [PATCH 19/22] change wrapping in h2
Signed-off-by: Ludovic Orban
---
.../jetty/http2/server/HTTP2ServerConnectionFactory.java | 3 +--
.../jetty/http2/server/internal/HttpStreamOverHTTP2.java | 5 +++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
index a254f4970e93..498b54fd9dbb 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java
@@ -156,8 +156,7 @@ public void onDataAvailable(Stream stream)
@Override
public void onReset(Stream stream, ResetFrame frame, Callback callback)
{
- EofException failure = new EofException("Reset " + ErrorCode.toString(frame.getError(), null));
- failure.initCause(new EOFException()); // reset marker
+ EOFException failure = new EOFException("Reset " + ErrorCode.toString(frame.getError(), null));
onFailure(stream, failure, callback);
}
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
index 0d9be074822a..f03e9479843f 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
@@ -39,6 +39,7 @@
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpStream;
import org.eclipse.jetty.server.Request;
@@ -588,8 +589,8 @@ public void onTimeout(TimeoutException timeout, BiConsumer co
@Override
public Runnable onFailure(Throwable failure, Callback callback)
{
- boolean remote = failure != null && failure.getCause() instanceof EOFException;
- Runnable runnable = remote ? _httpChannel.onRemoteFailure(failure) : _httpChannel.onFailure(failure);
+ boolean remote = failure instanceof EOFException;
+ Runnable runnable = remote ? _httpChannel.onRemoteFailure(new EofException(failure)) : _httpChannel.onFailure(failure);
return () ->
{
if (runnable != null)
From c0458a7b3eb3074a3027b828934da89b0292b357 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 17:57:48 +0200
Subject: [PATCH 20/22] change wrapping in h3
Signed-off-by: Ludovic Orban
---
.../jetty/http3/HTTP3StreamConnection.java | 14 +++--------
.../server/internal/HttpStreamOverHTTP3.java | 5 ++--
.../jetty/quic/common/QuicStreamEndPoint.java | 25 +++++++++++--------
3 files changed, 20 insertions(+), 24 deletions(-)
diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
index 29542da3d1eb..52b0bd715e9d 100644
--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3StreamConnection.java
@@ -14,7 +14,6 @@
package org.eclipse.jetty.http3;
import java.io.IOException;
-import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
@@ -275,7 +274,7 @@ private void tryReleaseInputBuffer(boolean force)
}
}
- private MessageParser.Result parseAndFill(boolean setFillInterest)
+ private MessageParser.Result parseAndFill(boolean setFillInterest) throws IOException
{
try
{
@@ -336,16 +335,9 @@ private MessageParser.Result parseAndFill(boolean setFillInterest)
}
}
- private int fill(ByteBuffer byteBuffer)
+ private int fill(ByteBuffer byteBuffer) throws IOException
{
- try
- {
- return getEndPoint().fill(byteBuffer);
- }
- catch (IOException x)
- {
- throw new UncheckedIOException(x.getMessage(), x);
- }
+ return getEndPoint().fill(byteBuffer);
}
private void processHeaders(HeadersFrame frame, boolean wasBlocked, Runnable delegate)
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
index 5299ac9a9230..33e9828772d8 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
@@ -34,6 +34,7 @@
import org.eclipse.jetty.http3.frames.DataFrame;
import org.eclipse.jetty.http3.frames.HeadersFrame;
import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpStream;
import org.eclipse.jetty.server.Request;
@@ -538,7 +539,7 @@ public Runnable onFailure(Throwable failure)
}
connection.onFailure(failure);
- boolean remote = failure != null && failure.getCause() instanceof EOFException;
- return remote ? httpChannel.onRemoteFailure(failure) : httpChannel.onFailure(failure);
+ boolean remote = failure instanceof EOFException;
+ return remote ? httpChannel.onRemoteFailure(new EofException(failure)) : httpChannel.onFailure(failure);
}
}
diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
index e40b1b9a72f2..227af953034c 100644
--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
+++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java
@@ -13,6 +13,7 @@
package org.eclipse.jetty.quic.common;
+import java.io.EOFException;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
@@ -266,23 +267,25 @@ public boolean onReadable()
}
else
{
- QuicStreamEndPoint streamEndPoint = getQuicSession().getStreamEndPoint(streamId);
- if (streamEndPoint.isStreamFinished())
+ if (isStreamFinished())
{
- // Check if the stream was finished because of a reset frame.
- Throwable x = null;
+ // Check if the stream was finished normally.
try
{
- streamEndPoint.fill(EMPTY_WRITABLE_BUFFER);
+ fill(EMPTY_WRITABLE_BUFFER);
}
- catch (IOException ioe)
+ catch (EOFException x)
{
- x = ioe;
+ // Got reset.
+ getFillInterest().onFail(x);
+ getQuicSession().onFailure(x);
+ }
+ catch (Throwable x)
+ {
+ EofException e = new EofException(x);
+ getFillInterest().onFail(e);
+ getQuicSession().onFailure(e);
}
-
- EofException e = new EofException(x);
- streamEndPoint.getFillInterest().onFail(e);
- streamEndPoint.getQuicSession().onFailure(e);
}
}
return interested;
From a428f3ce64c46e20823ea30abfb85b34054c0070 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 19:21:23 +0200
Subject: [PATCH 21/22] refactor ee* tests
Signed-off-by: Ludovic Orban
---
.../test/client/transport/AbstractTest.java | 111 +-----------
.../client/transport/AsyncIOServletTest.java | 89 ----------
.../transport/Http2AsyncIOServletTest.java | 151 ++++++++++++++++
.../transport/Http3AsyncIOServletTest.java | 161 ++++++++++++++++++
.../test/client/transport/AbstractTest.java | 130 +-------------
.../client/transport/AsyncIOServletTest.java | 92 ----------
.../transport/Http2AsyncIOServletTest.java | 150 ++++++++++++++++
.../transport/Http3AsyncIOServletTest.java | 161 ++++++++++++++++++
8 files changed, 627 insertions(+), 418 deletions(-)
create mode 100644 jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http2AsyncIOServletTest.java
create mode 100644 jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http3AsyncIOServletTest.java
create mode 100644 jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http2AsyncIOServletTest.java
create mode 100644 jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http3AsyncIOServletTest.java
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
index 104005b44619..85ab339dc865 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
@@ -14,18 +14,13 @@
package org.eclipse.jetty.ee10.test.client.transport;
import java.io.InputStream;
-import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Collection;
import java.util.EnumSet;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import jakarta.servlet.http.HttpServlet;
@@ -42,16 +37,12 @@
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
-import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
-import org.eclipse.jetty.http3.HTTP3ErrorCode;
import org.eclipse.jetty.http3.client.HTTP3Client;
import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3;
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
@@ -72,8 +63,6 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -96,11 +85,6 @@ public class AbstractTest
protected ServletContextHandler servletContextHandler;
protected HttpClient client;
- private HTTP2Client http2Client;
- private final Map h2Streams = new HashMap<>();
- private HTTP3Client http3Client;
- private final Map h3Streams = new HashMap<>();
-
public static Collection transports()
{
EnumSet transports = EnumSet.allOf(Transport.class);
@@ -131,14 +115,6 @@ public static Collection transportsSecure()
return transports;
}
- public static Collection transportsWithStreams()
- {
- EnumSet transports = EnumSet.of(Transport.H2C, Transport.H3);
- if ("ci".equals(System.getProperty("env")))
- transports.remove(Transport.H3);
- return transports;
- }
-
@BeforeEach
public void prepare()
{
@@ -148,8 +124,6 @@ public void prepare()
@AfterEach
public void dispose()
{
- h2Streams.clear();
- h3Streams.clear();
LifeCycle.stop(client);
LifeCycle.stop(server);
}
@@ -228,87 +202,6 @@ protected void startClient(Transport transport, Consumer consumer) t
client.start();
}
- protected long newRequestOnStream(Transport transport) throws Exception
- {
- switch (transport)
- {
- case H2C, H2 ->
- {
- return sendHeadersWithNewH2Stream();
- }
- case H3 ->
- {
- return sendHeadersWithNewH3Stream();
- }
- default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
- }
- }
-
- private int sendHeadersWithNewH2Stream() throws Exception
- {
- org.eclipse.jetty.http2.api.Session session = newHttp2ClientSession(new org.eclipse.jetty.http2.api.Session.Listener() {});
- MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
- HeadersFrame frame = new HeadersFrame(metaData, null, false);
- FuturePromise promise = new FuturePromise<>();
- session.newStream(frame, promise, null);
- org.eclipse.jetty.http2.api.Stream stream = promise.get(5, TimeUnit.SECONDS);
- int streamId = stream.getId();
- h2Streams.put(streamId, stream);
- return streamId;
- }
-
- private long sendHeadersWithNewH3Stream() throws Exception
- {
- org.eclipse.jetty.http3.api.Session.Client session = newHttp3ClientSession(new org.eclipse.jetty.http3.api.Session.Client.Listener() {});
- MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
- CompletableFuture cf = session.newRequest(new org.eclipse.jetty.http3.frames.HeadersFrame(metaData, false), null);
- org.eclipse.jetty.http3.api.Stream stream = cf.get(5, TimeUnit.SECONDS);
- long streamId = stream.getId();
- h3Streams.put(streamId, stream);
- return streamId;
- }
-
- protected void resetStream(Transport transport, long streamId)
- {
- switch (transport)
- {
- case H2C, H2 -> resetH2Stream((int)streamId);
- case H3 -> resetH3Stream(streamId);
- default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
- }
- }
-
- private void resetH2Stream(int streamId)
- {
- org.eclipse.jetty.http2.api.Stream stream = h2Streams.get(streamId);
- stream.reset(new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
- }
-
- private void resetH3Stream(long streamId)
- {
- org.eclipse.jetty.http3.api.Stream stream = h3Streams.get(streamId);
- stream.reset(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), new Exception(getClass().getSimpleName() + " reset"));
- }
-
- private org.eclipse.jetty.http2.api.Session newHttp2ClientSession(org.eclipse.jetty.http2.api.Session.Listener listener) throws Exception
- {
- String host = "localhost";
- int port = ((NetworkConnector)connector).getLocalPort();
- InetSocketAddress address = new InetSocketAddress(host, port);
- FuturePromise promise = new FuturePromise<>();
- http2Client.connect(address, listener, promise);
- return promise.get(5, TimeUnit.SECONDS);
- }
-
- private org.eclipse.jetty.http3.api.Session.Client newHttp3ClientSession(org.eclipse.jetty.http3.api.Session.Client.Listener listener) throws Exception
- {
- String host = "localhost";
- int port = ((NetworkConnector)connector).getLocalPort();
- InetSocketAddress address = new InetSocketAddress(host, port);
- CompletableFuture cf = http3Client.connect(address, listener);
- return cf.get(5, TimeUnit.SECONDS);
- }
-
protected MetaData.Request newRequest(String method, HttpFields fields)
{
return newRequest(method, "/", fields);
@@ -395,7 +288,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(newSslContextFactoryClient());
- http2Client = new HTTP2Client(clientConnector);
+ HTTP2Client http2Client = new HTTP2Client(clientConnector);
yield new HttpClientTransportOverHTTP2(http2Client);
}
case H3 ->
@@ -405,7 +298,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
SslContextFactory.Client sslContextFactory = newSslContextFactoryClient();
clientConnector.setSslContextFactory(sslContextFactory);
Path clientPemDirectory = Files.createDirectories(pemDir.resolve("client"));
- http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
+ HTTP3Client http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
yield new HttpClientTransportOverHTTP3(http3Client);
}
case FCGI -> new HttpClientTransportOverFCGI(1, "");
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
index aabe1d99071e..41472d1fd7c0 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AsyncIOServletTest.java
@@ -34,8 +34,6 @@
import java.util.concurrent.atomic.AtomicReference;
import jakarta.servlet.AsyncContext;
-import jakarta.servlet.AsyncEvent;
-import jakarta.servlet.AsyncListener;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
@@ -85,7 +83,6 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -1537,92 +1534,6 @@ public void onError(Throwable x)
assertEquals(HttpStatus.NO_CONTENT_204, response.getStatus());
}
- @ParameterizedTest
- @MethodSource("transportsWithStreams")
- public void testStartAsyncThenClientResetRemoteErrorDoesNotify(Transport transport) throws Exception
- {
- testStartAsyncThenClientResetRemoteErrorNotification(transport, true);
- }
-
- @ParameterizedTest
- @MethodSource("transportsWithStreams")
- public void testStartAsyncThenClientResetRemoteErrorDoesNotNotify(Transport transport) throws Exception
- {
- testStartAsyncThenClientResetRemoteErrorNotification(transport, false);
- }
-
- private void testStartAsyncThenClientResetRemoteErrorNotification(Transport transport, boolean notify) throws Exception
- {
- httpConfig.setNotifyRemoteAsyncErrors(notify);
-
- AtomicReference errorAsyncEventRef = new AtomicReference<>();
- AtomicReference responseRef = new AtomicReference<>();
- CountDownLatch latch = new CountDownLatch(1);
- start(transport, new HttpServlet()
- {
- @Override
- protected void service(HttpServletRequest request, HttpServletResponse response)
- {
- AsyncContext asyncContext = request.startAsync();
- asyncContext.addListener(new AsyncListener()
- {
- @Override
- public void onComplete(AsyncEvent event)
- {
- }
-
- @Override
- public void onTimeout(AsyncEvent event)
- {
- }
-
- @Override
- public void onError(AsyncEvent event)
- {
- errorAsyncEventRef.set(event);
- }
-
- @Override
- public void onStartAsync(AsyncEvent event)
- {
- }
- });
- asyncContext.setTimeout(0);
- responseRef.set(response);
- latch.countDown();
- }
- });
-
- long streamId = newRequestOnStream(transport);
-
- // Wait for the server to be in ASYNC_WAIT.
- assertTrue(latch.await(5, TimeUnit.SECONDS));
- sleep(500);
-
- resetStream(transport, streamId);
-
- if (notify)
- // Wait for the reset to be notified to the async context listener.
- await().atMost(5, TimeUnit.SECONDS).until(() ->
- {
- AsyncEvent asyncEvent = errorAsyncEventRef.get();
- return asyncEvent == null ? null : asyncEvent.getThrowable();
- }, instanceOf(EofException.class));
- else
- // Wait for the reset to NOT be notified to the failure listener.
- await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
-
- ServletOutputStream output = responseRef.get().getOutputStream();
-
- assertThrows(IOException.class,
- () ->
- {
- // Large writes or explicit flush() must
- // fail because the stream has been reset.
- output.flush();
- });
- }
-
private static class Listener implements ReadListener, WriteListener
{
private final Executor executor = Executors.newFixedThreadPool(32);
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http2AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http2AsyncIOServletTest.java
new file mode 100644
index 000000000000..c55aadae64c9
--- /dev/null
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http2AsyncIOServletTest.java
@@ -0,0 +1,151 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.ee10.test.client.transport;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class Http2AsyncIOServletTest
+{
+ private Server server;
+ private ServerConnector connector;
+ private HTTP2Client client;
+
+ private void start(HttpConfiguration httpConfig, HttpServlet httpServlet) throws Exception
+ {
+ server = new Server();
+ connector = new ServerConnector(server, 1, 1, new HTTP2CServerConnectionFactory(httpConfig));
+ server.addConnector(connector);
+ ServletContextHandler servletContextHandler = new ServletContextHandler("/");
+ servletContextHandler.addServlet(new ServletHolder(httpServlet), "/*");
+ server.setHandler(servletContextHandler);
+ server.start();
+
+ client = new HTTP2Client();
+ client.start();
+ }
+
+ @AfterEach
+ public void tearDown()
+ {
+ LifeCycle.stop(client);
+ LifeCycle.stop(server);
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
+ {
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ httpConfig.setNotifyRemoteAsyncErrors(notify);
+
+ AtomicReference errorAsyncEventRef = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ start(httpConfig, new HttpServlet()
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener()
+ {
+ @Override
+ public void onComplete(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event)
+ {
+ errorAsyncEventRef.set(event);
+ asyncContext.complete();
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event)
+ {
+ }
+ });
+ asyncContext.setTimeout(0);
+ latch.countDown();
+ }
+ });
+
+ InetSocketAddress address = new InetSocketAddress("localhost", connector.getLocalPort());
+ FuturePromise sessionPromise = new FuturePromise<>();
+ client.connect(address, new Session.Listener() {}, sessionPromise);
+ Session session = sessionPromise.get(5, TimeUnit.SECONDS);
+ MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("/"), HttpVersion.HTTP_2, HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, false);
+ Stream stream = session.newStream(frame, null).get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be in ASYNC_WAIT.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Thread.sleep(500);
+
+ stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code));
+
+ if (notify)
+ // Wait for the reset to be notified to the async context listener.
+ await().atMost(5, TimeUnit.SECONDS).until(() ->
+ {
+ AsyncEvent asyncEvent = errorAsyncEventRef.get();
+ return asyncEvent == null ? null : asyncEvent.getThrowable();
+ }, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
+ }
+}
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http3AsyncIOServletTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http3AsyncIOServletTest.java
new file mode 100644
index 000000000000..64d5eaf00bbc
--- /dev/null
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/Http3AsyncIOServletTest.java
@@ -0,0 +1,161 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.ee10.test.client.transport;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http3.HTTP3ErrorCode;
+import org.eclipse.jetty.http3.api.Stream;
+import org.eclipse.jetty.http3.client.HTTP3Client;
+import org.eclipse.jetty.http3.frames.HeadersFrame;
+import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.quic.client.ClientQuicConfiguration;
+import org.eclipse.jetty.quic.server.QuicServerConnector;
+import org.eclipse.jetty.quic.server.ServerQuicConfiguration;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.toolchain.test.MavenPaths;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.awaitility.Awaitility.await;
+import static org.eclipse.jetty.http3.api.Session.Client;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(WorkDirExtension.class)
+public class Http3AsyncIOServletTest
+{
+ public WorkDir workDir;
+
+ private Server server;
+ private QuicServerConnector connector;
+ private HTTP3Client client;
+
+ private void start(HttpConfiguration httpConfig, HttpServlet httpServlet) throws Exception
+ {
+ server = new Server();
+ SslContextFactory.Server serverSslContextFactory = new SslContextFactory.Server();
+ serverSslContextFactory.setKeyStorePath(MavenPaths.findTestResourceFile("keystore.p12").toString());
+ serverSslContextFactory.setKeyStorePassword("storepwd");
+ ServerQuicConfiguration serverQuicConfiguration = new ServerQuicConfiguration(serverSslContextFactory, workDir.getEmptyPathDir());
+ connector = new QuicServerConnector(server, serverQuicConfiguration, new HTTP3ServerConnectionFactory(serverQuicConfiguration, httpConfig));
+ server.addConnector(connector);
+ ServletContextHandler servletContextHandler = new ServletContextHandler("/");
+ servletContextHandler.addServlet(new ServletHolder(httpServlet), "/*");
+ server.setHandler(servletContextHandler);
+ server.start();
+
+ client = new HTTP3Client(new ClientQuicConfiguration(new SslContextFactory.Client(true), null));
+ client.start();
+ }
+
+ @AfterEach
+ public void tearDown()
+ {
+ LifeCycle.stop(client);
+ LifeCycle.stop(server);
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
+ {
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ httpConfig.setNotifyRemoteAsyncErrors(notify);
+
+ AtomicReference errorAsyncEventRef = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ start(httpConfig, new HttpServlet()
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener()
+ {
+ @Override
+ public void onComplete(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event)
+ {
+ errorAsyncEventRef.set(event);
+ asyncContext.complete();
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event)
+ {
+ }
+ });
+ asyncContext.setTimeout(0);
+ latch.countDown();
+ }
+ });
+
+ InetSocketAddress address = new InetSocketAddress("localhost", connector.getLocalPort());
+ Client session = client.connect(address, new Client.Listener() {}).get(5, TimeUnit.SECONDS);
+ MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("/"), HttpVersion.HTTP_3, HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, false);
+ Stream stream = session.newRequest(frame, null).get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be in ASYNC_WAIT.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Thread.sleep(500);
+
+ stream.reset(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), new Exception());
+
+ if (notify)
+ // Wait for the reset to be notified to the async context listener.
+ await().atMost(5, TimeUnit.SECONDS).until(() ->
+ {
+ AsyncEvent asyncEvent = errorAsyncEventRef.get();
+ return asyncEvent == null ? null : asyncEvent.getThrowable();
+ }, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
+ }
+}
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
index d3799a0a84de..63efe5877d06 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AbstractTest.java
@@ -15,7 +15,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -25,12 +24,8 @@
import java.security.cert.CertificateException;
import java.util.Collection;
import java.util.EnumSet;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServlet;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
@@ -41,22 +36,12 @@
import org.eclipse.jetty.ee9.servlet.ServletHolder;
import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI;
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
-import org.eclipse.jetty.http.HostPortHttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
-import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
-import org.eclipse.jetty.http3.HTTP3ErrorCode;
-import org.eclipse.jetty.http3.api.Stream;
import org.eclipse.jetty.http3.client.HTTP3Client;
import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3;
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
@@ -77,8 +62,6 @@
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -101,11 +84,6 @@ public class AbstractTest
protected ServletContextHandler servletContextHandler;
protected HttpClient client;
- private HTTP2Client http2Client;
- private final Map h2Streams = new HashMap<>();
- private HTTP3Client http3Client;
- private final Map h3Streams = new HashMap<>();
-
public static Collection transports()
{
EnumSet transports = EnumSet.allOf(Transport.class);
@@ -128,14 +106,6 @@ public static Collection transportsWithPushSupport()
return transports;
}
- public static Collection transportsWithStreams()
- {
- EnumSet transports = EnumSet.of(Transport.H2C, Transport.H3);
- if ("ci".equals(System.getProperty("env")))
- transports.remove(Transport.H3);
- return transports;
- }
-
@BeforeEach
public void prepare()
{
@@ -145,8 +115,6 @@ public void prepare()
@AfterEach
public void dispose()
{
- h2Streams.clear();
- h3Streams.clear();
LifeCycle.stop(client);
LifeCycle.stop(server);
}
@@ -228,100 +196,6 @@ protected void startClient(Transport transport) throws Exception
client.start();
}
- protected long newRequestOnStream(Transport transport) throws Exception
- {
- switch (transport)
- {
- case H2C, H2 ->
- {
- return sendHeadersWithNewH2Stream();
- }
- case H3 ->
- {
- return sendHeadersWithNewH3Stream();
- }
- default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
- }
- }
-
- private int sendHeadersWithNewH2Stream() throws Exception
- {
- org.eclipse.jetty.http2.api.Session session = newHttp2ClientSession(new org.eclipse.jetty.http2.api.Session.Listener() {});
- MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
- HeadersFrame frame = new HeadersFrame(metaData, null, false);
- FuturePromise promise = new FuturePromise<>();
- session.newStream(frame, promise, null);
- org.eclipse.jetty.http2.api.Stream stream = promise.get(5, TimeUnit.SECONDS);
- int streamId = stream.getId();
- h2Streams.put(streamId, stream);
- return streamId;
- }
-
- private long sendHeadersWithNewH3Stream() throws Exception
- {
- org.eclipse.jetty.http3.api.Session.Client session = newHttp3ClientSession(new org.eclipse.jetty.http3.api.Session.Client.Listener() {});
- MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
- CompletableFuture cf = session.newRequest(new org.eclipse.jetty.http3.frames.HeadersFrame(metaData, false), null);
- org.eclipse.jetty.http3.api.Stream stream = cf.get(5, TimeUnit.SECONDS);
- long streamId = stream.getId();
- h3Streams.put(streamId, stream);
- return streamId;
- }
-
- protected void resetStream(Transport transport, long streamId)
- {
- switch (transport)
- {
- case H2C, H2 -> resetH2Stream((int)streamId);
- case H3 -> resetH3Stream(streamId);
- default -> throw new IllegalArgumentException("Transport does not support streams: " + transport);
- }
- }
-
- private void resetH2Stream(int streamId)
- {
- org.eclipse.jetty.http2.api.Stream stream = h2Streams.get(streamId);
- stream.reset(new ResetFrame(streamId, ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
- }
-
- private void resetH3Stream(long streamId)
- {
- org.eclipse.jetty.http3.api.Stream stream = h3Streams.get(streamId);
- stream.reset(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), new Exception(getClass().getSimpleName() + " reset"));
- }
-
- private org.eclipse.jetty.http2.api.Session newHttp2ClientSession(org.eclipse.jetty.http2.api.Session.Listener listener) throws Exception
- {
- String host = "localhost";
- int port = ((NetworkConnector)connector).getLocalPort();
- InetSocketAddress address = new InetSocketAddress(host, port);
- FuturePromise promise = new FuturePromise<>();
- http2Client.connect(address, listener, promise);
- return promise.get(5, TimeUnit.SECONDS);
- }
-
- private org.eclipse.jetty.http3.api.Session.Client newHttp3ClientSession(org.eclipse.jetty.http3.api.Session.Client.Listener listener) throws Exception
- {
- String host = "localhost";
- int port = ((NetworkConnector)connector).getLocalPort();
- InetSocketAddress address = new InetSocketAddress(host, port);
- CompletableFuture cf = http3Client.connect(address, listener);
- return cf.get(5, TimeUnit.SECONDS);
- }
-
- protected MetaData.Request newRequest(String method, HttpFields fields)
- {
- return newRequest(method, "/", fields);
- }
-
- protected MetaData.Request newRequest(String method, String path, HttpFields fields)
- {
- String host = "localhost";
- int port = ((NetworkConnector)connector).getLocalPort();
- String authority = host + ":" + port;
- return new MetaData.Request(method, HttpScheme.HTTP.asString(), new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields, -1);
- }
-
public AbstractConnector newConnector(Transport transport, Server server)
{
return switch (transport)
@@ -394,7 +268,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(newSslContextFactoryClient());
- http2Client = new HTTP2Client(clientConnector);
+ HTTP2Client http2Client = new HTTP2Client(clientConnector);
yield new HttpClientTransportOverHTTP2(http2Client);
}
case H3 ->
@@ -404,7 +278,7 @@ protected HttpClientTransport newHttpClientTransport(Transport transport) throws
SslContextFactory.Client sslContextFactory = newSslContextFactoryClient();
clientConnector.setSslContextFactory(sslContextFactory);
Path clientPemDirectory = Files.createDirectories(pemDir.resolve("client"));
- http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
+ HTTP3Client http3Client = new HTTP3Client(new ClientQuicConfiguration(sslContextFactory, clientPemDirectory));
yield new HttpClientTransportOverHTTP3(http3Client);
}
case FCGI -> new HttpClientTransportOverFCGI(1, "");
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
index e4a674b6b846..4a10ca2b01fe 100644
--- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/AsyncIOServletTest.java
@@ -35,8 +35,6 @@
import java.util.concurrent.atomic.AtomicReference;
import jakarta.servlet.AsyncContext;
-import jakarta.servlet.AsyncEvent;
-import jakarta.servlet.AsyncListener;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
@@ -87,7 +85,6 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -1864,95 +1861,6 @@ public void onError(Throwable x)
assertEquals(1, allDataReadCount.get());
}
- @ParameterizedTest
- @MethodSource("transportsWithStreams")
- public void testStartAsyncThenClientResetRemoteErrorDoesNotify(Transport transport) throws Exception
- {
- testStartAsyncThenClientResetRemoteErrorNotification(transport, true);
- }
-
- @ParameterizedTest
- @MethodSource("transportsWithStreams")
- public void testStartAsyncThenClientResetRemoteErrorDoesNotNotify(Transport transport) throws Exception
- {
- testStartAsyncThenClientResetRemoteErrorNotification(transport, false);
- }
-
- private void testStartAsyncThenClientResetRemoteErrorNotification(Transport transport, boolean notify) throws Exception
- {
- httpConfig.setNotifyRemoteAsyncErrors(notify);
-
- AtomicReference errorAsyncEventRef = new AtomicReference<>();
- AtomicReference responseRef = new AtomicReference<>();
- CountDownLatch latch = new CountDownLatch(1);
- start(transport, new HttpServlet()
- {
- @Override
- protected void service(HttpServletRequest request, HttpServletResponse response)
- {
- AsyncContext asyncContext = request.startAsync();
- asyncContext.addListener(new AsyncListener()
- {
- @Override
- public void onComplete(AsyncEvent event)
- {
- }
-
- @Override
- public void onTimeout(AsyncEvent event)
- {
- }
-
- @Override
- public void onError(AsyncEvent event)
- {
- errorAsyncEventRef.set(event);
- }
-
- @Override
- public void onStartAsync(AsyncEvent event)
- {
- }
- });
- asyncContext.setTimeout(0);
- responseRef.set(response);
- latch.countDown();
- }
- });
-
- long streamId = newRequestOnStream(transport);
-
- // Wait for the server to be in ASYNC_WAIT.
- assertTrue(latch.await(5, TimeUnit.SECONDS));
- sleep(500);
-
- resetStream(transport, streamId);
-
- if (notify)
- // Wait for the reset to be notified to the async context listener.
- await().atMost(5, TimeUnit.SECONDS).until(() ->
- {
- AsyncEvent asyncEvent = errorAsyncEventRef.get();
- return asyncEvent == null ? null : asyncEvent.getThrowable();
- }, instanceOf(EofException.class));
- else
- // Wait for the reset to NOT be notified to the failure listener.
- await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
-
- ServletOutputStream output = responseRef.get().getOutputStream();
-
- if (notify)
- output.flush();
- else
- assertThrows(IOException.class,
- () ->
- {
- // Large writes or explicit flush() must
- // fail because the stream has been reset.
- output.flush();
- });
- }
-
private static class Listener implements ReadListener, WriteListener
{
private final Executor executor = Executors.newFixedThreadPool(32);
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http2AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http2AsyncIOServletTest.java
new file mode 100644
index 000000000000..12e6a7cfef6c
--- /dev/null
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http2AsyncIOServletTest.java
@@ -0,0 +1,150 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.ee9.test.client.transport;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee9.servlet.ServletHolder;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class Http2AsyncIOServletTest
+{
+ private Server server;
+ private ServerConnector connector;
+ private HTTP2Client client;
+
+ private void start(HttpConfiguration httpConfig, HttpServlet httpServlet) throws Exception
+ {
+ server = new Server();
+ connector = new ServerConnector(server, 1, 1, new HTTP2CServerConnectionFactory(httpConfig));
+ server.addConnector(connector);
+ ServletContextHandler servletContextHandler = new ServletContextHandler(server, "/");
+ servletContextHandler.addServlet(new ServletHolder(httpServlet), "/*");
+ server.start();
+
+ client = new HTTP2Client();
+ client.start();
+ }
+
+ @AfterEach
+ public void tearDown()
+ {
+ LifeCycle.stop(client);
+ LifeCycle.stop(server);
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
+ {
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ httpConfig.setNotifyRemoteAsyncErrors(notify);
+
+ AtomicReference errorAsyncEventRef = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ start(httpConfig, new HttpServlet()
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener()
+ {
+ @Override
+ public void onComplete(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event)
+ {
+ errorAsyncEventRef.set(event);
+ asyncContext.complete();
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event)
+ {
+ }
+ });
+ asyncContext.setTimeout(0);
+ latch.countDown();
+ }
+ });
+
+ InetSocketAddress address = new InetSocketAddress("localhost", connector.getLocalPort());
+ FuturePromise sessionPromise = new FuturePromise<>();
+ client.connect(address, new Session.Listener() {}, sessionPromise);
+ Session session = sessionPromise.get(5, TimeUnit.SECONDS);
+ MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("/"), HttpVersion.HTTP_2, HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, null, false);
+ Stream stream = session.newStream(frame, null).get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be in ASYNC_WAIT.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Thread.sleep(500);
+
+ stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code));
+
+ if (notify)
+ // Wait for the reset to be notified to the async context listener.
+ await().atMost(5, TimeUnit.SECONDS).until(() ->
+ {
+ AsyncEvent asyncEvent = errorAsyncEventRef.get();
+ return asyncEvent == null ? null : asyncEvent.getThrowable();
+ }, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
+ }
+}
diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http3AsyncIOServletTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http3AsyncIOServletTest.java
new file mode 100644
index 000000000000..737e29e4dd03
--- /dev/null
+++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-client-transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/Http3AsyncIOServletTest.java
@@ -0,0 +1,161 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.ee9.test.client.transport;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee9.servlet.ServletHolder;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http3.HTTP3ErrorCode;
+import org.eclipse.jetty.http3.api.Session;
+import org.eclipse.jetty.http3.api.Stream;
+import org.eclipse.jetty.http3.client.HTTP3Client;
+import org.eclipse.jetty.http3.frames.HeadersFrame;
+import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.quic.client.ClientQuicConfiguration;
+import org.eclipse.jetty.quic.server.QuicServerConnector;
+import org.eclipse.jetty.quic.server.ServerQuicConfiguration;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.toolchain.test.MavenPaths;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.awaitility.Awaitility.await;
+import static org.eclipse.jetty.http3.api.Session.Client;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(WorkDirExtension.class)
+public class Http3AsyncIOServletTest
+{
+ public WorkDir workDir;
+
+ private Server server;
+ private QuicServerConnector connector;
+ private HTTP3Client client;
+
+ private void start(HttpConfiguration httpConfig, HttpServlet httpServlet) throws Exception
+ {
+ server = new Server();
+ SslContextFactory.Server serverSslContextFactory = new SslContextFactory.Server();
+ serverSslContextFactory.setKeyStorePath(MavenPaths.findTestResourceFile("keystore.p12").toString());
+ serverSslContextFactory.setKeyStorePassword("storepwd");
+ ServerQuicConfiguration serverQuicConfiguration = new ServerQuicConfiguration(serverSslContextFactory, workDir.getEmptyPathDir());
+ connector = new QuicServerConnector(server, serverQuicConfiguration, new HTTP3ServerConnectionFactory(serverQuicConfiguration, httpConfig));
+ server.addConnector(connector);
+ ServletContextHandler servletContextHandler = new ServletContextHandler(server, "/");
+ servletContextHandler.addServlet(new ServletHolder(httpServlet), "/*");
+ server.start();
+
+ client = new HTTP3Client(new ClientQuicConfiguration(new SslContextFactory.Client(true), null));
+ client.start();
+ }
+
+ @AfterEach
+ public void tearDown()
+ {
+ LifeCycle.stop(client);
+ LifeCycle.stop(server);
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testStartAsyncThenClientResetRemoteErrorNotification(boolean notify) throws Exception
+ {
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ httpConfig.setNotifyRemoteAsyncErrors(notify);
+
+ AtomicReference errorAsyncEventRef = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ start(httpConfig, new HttpServlet()
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener()
+ {
+ @Override
+ public void onComplete(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event)
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event)
+ {
+ errorAsyncEventRef.set(event);
+ asyncContext.complete();
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event)
+ {
+ }
+ });
+ asyncContext.setTimeout(0);
+ latch.countDown();
+ }
+ });
+
+ InetSocketAddress address = new InetSocketAddress("localhost", connector.getLocalPort());
+ Session.Client session = client.connect(address, new Client.Listener() {}).get(5, TimeUnit.SECONDS);
+ MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("/"), HttpVersion.HTTP_3, HttpFields.EMPTY);
+ HeadersFrame frame = new HeadersFrame(metaData, false);
+ Stream stream = session.newRequest(frame, null).get(5, TimeUnit.SECONDS);
+
+ // Wait for the server to be in ASYNC_WAIT.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ Thread.sleep(500);
+
+ stream.reset(HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(), new Exception());
+
+ if (notify)
+ // Wait for the reset to be notified to the async context listener.
+ await().atMost(5, TimeUnit.SECONDS).until(() ->
+ {
+ AsyncEvent asyncEvent = errorAsyncEventRef.get();
+ return asyncEvent == null ? null : asyncEvent.getThrowable();
+ }, instanceOf(EofException.class));
+ else
+ // Wait for the reset to NOT be notified to the failure listener.
+ await().atMost(5, TimeUnit.SECONDS).during(1, TimeUnit.SECONDS).until(errorAsyncEventRef::get, nullValue());
+ }
+}
From bacbabeb06fd02ef49c6ef39be1656a902bd4e20 Mon Sep 17 00:00:00 2001
From: Ludovic Orban
Date: Tue, 18 Jun 2024 19:22:12 +0200
Subject: [PATCH 22/22] refactor ee* tests
Signed-off-by: Ludovic Orban
---
.../test/client/transport/AbstractTest.java | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
index 85ab339dc865..a04f32398b8e 100644
--- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
+++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/AbstractTest.java
@@ -32,11 +32,6 @@
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI;
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
-import org.eclipse.jetty.http.HostPortHttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
@@ -202,19 +197,6 @@ protected void startClient(Transport transport, Consumer consumer) t
client.start();
}
- protected MetaData.Request newRequest(String method, HttpFields fields)
- {
- return newRequest(method, "/", fields);
- }
-
- protected MetaData.Request newRequest(String method, String path, HttpFields fields)
- {
- String host = "localhost";
- int port = ((NetworkConnector)connector).getLocalPort();
- String authority = host + ":" + port;
- return new MetaData.Request(method, HttpScheme.HTTP.asString(), new HostPortHttpField(authority), path, HttpVersion.HTTP_2, fields, -1);
- }
-
public AbstractConnector newConnector(Transport transport, Server server)
{
return switch (transport)