From 82338660a8fbd8392defde92fa5130e8719c51b5 Mon Sep 17 00:00:00 2001
From: Olivier Lamy
Date: Mon, 25 Jul 2022 19:54:18 +1000
Subject: [PATCH 1/4] add jetty-ee8-proxy (#8332)
* restore ee8 proxy
Signed-off-by: Olivier Lamy
---
.github/workflows/codeql-analysis.yml | 6 +-
jetty-ee10/jetty-ee10-proxy/pom.xml | 2 +-
jetty-ee8/jetty-ee8-proxy/pom.xml | 79 +++++++++++++++++++
.../src/main/config/etc/jetty-ee8-proxy.xml | 35 ++++++++
.../src/main/config/modules/ee8-proxy.mod | 27 +++++++
jetty-ee8/pom.xml | 1 +
.../src/main/config/modules/ee9-proxy.mod | 2 +-
.../jetty/ee9/proxy/ClientAuthProxyTest.java | 14 ++--
.../ee9/proxy/ConnectHandlerSSLTest.java | 2 +-
.../ee9/proxy/ForwardProxyServerTest.java | 2 +-
.../ee9/proxy/ForwardProxyTLSServerTest.java | 10 +--
.../jetty/ee9/proxy/ProxyServletTest.java | 2 +-
12 files changed, 164 insertions(+), 18 deletions(-)
create mode 100644 jetty-ee8/jetty-ee8-proxy/pom.xml
create mode 100644 jetty-ee8/jetty-ee8-proxy/src/main/config/etc/jetty-ee8-proxy.xml
create mode 100644 jetty-ee8/jetty-ee8-proxy/src/main/config/modules/ee8-proxy.mod
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index ca71a3ce28a..46013d167c4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -67,8 +67,12 @@ jobs:
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
+ - name: Set up Maven
+ run:
+ mvn -e -B -V --show-version org.apache.maven.plugins:maven-wrapper-plugin:3.1.0:wrapper "-Dmaven=3.8.6"
+
- name: Clean install dependencies and build
- run: mvn clean install -DskipTests
+ run: ./mvnw clean install -DskipTests -V -B
# âšī¸ Command-line programs to run using the OS shell.
# đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
diff --git a/jetty-ee10/jetty-ee10-proxy/pom.xml b/jetty-ee10/jetty-ee10-proxy/pom.xml
index 808869d39a2..6f2ab96b61e 100644
--- a/jetty-ee10/jetty-ee10-proxy/pom.xml
+++ b/jetty-ee10/jetty-ee10-proxy/pom.xml
@@ -12,7 +12,7 @@
${project.groupId}.proxy
- org.eclipse.jetty.proxy.*
+ org.eclipse.jetty.ee10.proxy.*
diff --git a/jetty-ee8/jetty-ee8-proxy/pom.xml b/jetty-ee8/jetty-ee8-proxy/pom.xml
new file mode 100644
index 00000000000..8ddb2323f6f
--- /dev/null
+++ b/jetty-ee8/jetty-ee8-proxy/pom.xml
@@ -0,0 +1,79 @@
+
+
+ org.eclipse.jetty.ee8
+ jetty-ee8
+ 12.0.0-SNAPSHOT
+
+
+ 4.0.0
+ jetty-ee8-proxy
+ EE8 :: Jetty :: Proxy
+ Jetty Proxy
+
+
+ jetty-ee9-proxy
+ ${project.groupId}.proxy
+ org.eclipse.jetty.ee8.proxy.*
+
+
+
+
+ maven-surefire-plugin
+
+
+ @{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.ee8.proxy=org.eclipse.jetty.logging
+
+
+
+
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.eclipse.jetty.toolchain
+ jetty-servlet-api
+ provided
+
+
+ org.eclipse.jetty.ee8
+ jetty-ee8-servlet
+ provided
+
+
+ org.eclipse.jetty
+ jetty-util
+
+
+ org.eclipse.jetty
+ jetty-client
+
+
+ org.eclipse.jetty
+ jetty-util-ajax
+ test
+
+
+ org.eclipse.jetty
+ jetty-http-tools
+ test
+
+
+ org.eclipse.jetty
+ jetty-slf4j-impl
+ test
+
+
+ org.eclipse.jetty
+ jetty-rewrite
+ test
+
+
+ org.eclipse.jetty.toolchain
+ jetty-test-helper
+ test
+
+
+
diff --git a/jetty-ee8/jetty-ee8-proxy/src/main/config/etc/jetty-ee8-proxy.xml b/jetty-ee8/jetty-ee8-proxy/src/main/config/etc/jetty-ee8-proxy.xml
new file mode 100644
index 00000000000..5ed1d5837eb
--- /dev/null
+++ b/jetty-ee8/jetty-ee8-proxy/src/main/config/etc/jetty-ee8-proxy.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ maxThreads
+
+
+
+ maxConnections
+
+
+
+ idleTimeout
+
+
+
+ timeout
+
+
+
+
+
+
+
+
+
diff --git a/jetty-ee8/jetty-ee8-proxy/src/main/config/modules/ee8-proxy.mod b/jetty-ee8/jetty-ee8-proxy/src/main/config/modules/ee8-proxy.mod
new file mode 100644
index 00000000000..d2d675af92b
--- /dev/null
+++ b/jetty-ee8/jetty-ee8-proxy/src/main/config/modules/ee8-proxy.mod
@@ -0,0 +1,27 @@
+# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
+
+[description]
+Enables the Jetty Proxy service.
+Allows the server to act as a non-transparent proxy for browsers.
+
+[depend]
+ee8-servlet
+client
+
+[environment]
+ee8
+
+[lib]
+lib/jetty-ee8-proxy-${jetty.version}.jar
+
+[xml]
+etc/jetty-ee8-proxy.xml
+
+[ini-template]
+## Proxy Configuration
+# jetty.proxy.servletClass=org.eclipse.jetty.ee8.proxy.ProxyServlet
+# jetty.proxy.servletMapping=/*
+# jetty.proxy.maxThreads=128
+# jetty.proxy.maxConnections=256
+# jetty.proxy.idleTimeout=30000
+# jetty.proxy.timeout=60000
diff --git a/jetty-ee8/pom.xml b/jetty-ee8/pom.xml
index ca8d955742b..a04983010b4 100644
--- a/jetty-ee8/pom.xml
+++ b/jetty-ee8/pom.xml
@@ -34,6 +34,7 @@
jetty-ee8-jaasjetty-ee8-plusjetty-ee8-jndi
+ jetty-ee8-proxyjetty-ee8-annotationsjetty-ee8-websocketjetty-ee8-quickstart
diff --git a/jetty-ee9/jetty-ee9-proxy/src/main/config/modules/ee9-proxy.mod b/jetty-ee9/jetty-ee9-proxy/src/main/config/modules/ee9-proxy.mod
index 82a6ac58589..8243766f95b 100644
--- a/jetty-ee9/jetty-ee9-proxy/src/main/config/modules/ee9-proxy.mod
+++ b/jetty-ee9/jetty-ee9-proxy/src/main/config/modules/ee9-proxy.mod
@@ -19,7 +19,7 @@ etc/jetty-ee9-proxy.xml
[ini-template]
## Proxy Configuration
-# jetty.proxy.servletClass=org.eclipse.jetty.proxy.ProxyServlet
+# jetty.proxy.servletClass=org.eclipse.jetty.ee9.proxy.ProxyServlet
# jetty.proxy.servletMapping=/*
# jetty.proxy.maxThreads=128
# jetty.proxy.maxConnections=256
diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ClientAuthProxyTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ClientAuthProxyTest.java
index 256e8e01bab..c6fbb941fe6 100644
--- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ClientAuthProxyTest.java
+++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ClientAuthProxyTest.java
@@ -117,7 +117,7 @@ public class ClientAuthProxyTest
serverTLS.setSniRequired(false);
serverTLS.setNeedClientAuth(true);
// The KeyStore is also a TrustStore.
- serverTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/server_keystore.p12").getAbsolutePath());
+ serverTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/server_keystore.p12").getAbsolutePath());
serverTLS.setKeyStorePassword("storepwd");
serverTLS.setKeyStoreType("PKCS12");
@@ -166,7 +166,7 @@ public class ClientAuthProxyTest
proxyTLS.setSniRequired(false);
proxyTLS.setNeedClientAuth(true);
// The KeyStore is also a TrustStore.
- proxyTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath());
+ proxyTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/proxy_keystore.p12").getAbsolutePath());
proxyTLS.setKeyStorePassword("storepwd");
proxyTLS.setKeyStoreType("PKCS12");
@@ -188,7 +188,7 @@ public class ClientAuthProxyTest
SslContextFactory.Client clientTLS = new SslContextFactory.Client();
// Disable TLS-level hostname verification.
clientTLS.setEndpointIdentificationAlgorithm(null);
- clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/client_keystore.p12").getAbsolutePath());
+ clientTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/client_keystore.p12").getAbsolutePath());
clientTLS.setKeyStorePassword("storepwd");
clientTLS.setKeyStoreType("PKCS12");
ClientConnector connector = new ClientConnector();
@@ -261,7 +261,7 @@ public class ClientAuthProxyTest
SslContextFactory.Client clientTLS = new SslContextFactory.Client();
// Disable TLS-level hostname verification for this test.
clientTLS.setEndpointIdentificationAlgorithm(null);
- clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath());
+ clientTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/proxy_keystore.p12").getAbsolutePath());
clientTLS.setKeyStorePassword("storepwd");
clientTLS.setKeyStoreType("PKCS12");
clientTLS.setCertAlias(key + "_proxy");
@@ -327,7 +327,7 @@ public class ClientAuthProxyTest
};
// Disable TLS-level hostname verification for this test.
clientTLS.setEndpointIdentificationAlgorithm(null);
- clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath());
+ clientTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/proxy_keystore.p12").getAbsolutePath());
clientTLS.setKeyStorePassword("storepwd");
clientTLS.setKeyStoreType("PKCS12");
ClientConnector connector = new ClientConnector();
@@ -390,7 +390,7 @@ public class ClientAuthProxyTest
};
// Disable hostname verification is required.
clientTLS.setEndpointIdentificationAlgorithm(null);
- clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath());
+ clientTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/proxy_keystore.p12").getAbsolutePath());
clientTLS.setKeyStorePassword("storepwd");
clientTLS.setKeyStoreType("PKCS12");
ClientConnector connector = new ClientConnector();
@@ -440,7 +440,7 @@ public class ClientAuthProxyTest
{
// Disable TLS-level hostname verification for this test.
tls.setEndpointIdentificationAlgorithm(null);
- tls.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath());
+ tls.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_auth/proxy_keystore.p12").getAbsolutePath());
tls.setKeyStorePassword("storepwd");
tls.setKeyStoreType("PKCS12");
if (user != null)
diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ConnectHandlerSSLTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ConnectHandlerSSLTest.java
index 5b93a6a8a89..623638df23e 100644
--- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ConnectHandlerSSLTest.java
+++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ConnectHandlerSSLTest.java
@@ -48,7 +48,7 @@ public class ConnectHandlerSSLTest extends AbstractConnectHandlerTest
public void prepare() throws Exception
{
sslContextFactory = new SslContextFactory.Server();
- String keyStorePath = MavenTestingUtils.getTestResourceFile("server_keystore.p12").getAbsolutePath();
+ String keyStorePath = MavenTestingUtils.getTargetFile("test-classes/server_keystore.p12").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
server = new Server();
diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyServerTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyServerTest.java
index 562fa6adc7a..3fdee000488 100644
--- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyServerTest.java
+++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyServerTest.java
@@ -72,7 +72,7 @@ public class ForwardProxyServerTest
private static SslContextFactory.Server newServerSslContextFactory()
{
SslContextFactory.Server serverTLS = new SslContextFactory.Server();
- String keyStorePath = MavenTestingUtils.getTestResourceFile("server_keystore.p12").getAbsolutePath();
+ String keyStorePath = MavenTestingUtils.getTargetFile("test-classes/server_keystore.p12").getAbsolutePath();
serverTLS.setKeyStorePath(keyStorePath);
serverTLS.setKeyStorePassword("storepwd");
return serverTLS;
diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyTLSServerTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyTLSServerTest.java
index 7f6dd16f66f..b806d09a9d9 100644
--- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyTLSServerTest.java
+++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ForwardProxyTLSServerTest.java
@@ -144,7 +144,7 @@ public class ForwardProxyTLSServerTest
private static SslContextFactory.Server newServerSslContextFactory()
{
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
- String keyStorePath = MavenTestingUtils.getTestResourceFile("server_keystore.p12").getAbsolutePath();
+ String keyStorePath = MavenTestingUtils.getTargetFile("test-classes/server_keystore.p12").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
return sslContextFactory;
@@ -153,7 +153,7 @@ public class ForwardProxyTLSServerTest
private static SslContextFactory.Server newProxySslContextFactory()
{
SslContextFactory.Server proxyTLS = new SslContextFactory.Server();
- String keyStorePath = MavenTestingUtils.getTestResourceFile("proxy_keystore.p12").getAbsolutePath();
+ String keyStorePath = MavenTestingUtils.getTargetFile("test-classes/proxy_keystore.p12").getAbsolutePath();
proxyTLS.setKeyStorePath(keyStorePath);
proxyTLS.setKeyStorePassword("storepwd");
return proxyTLS;
@@ -692,7 +692,7 @@ public class ForwardProxyTLSServerTest
return keyManagers;
}
};
- clientSslContextFactory.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_keystore.p12").getAbsolutePath());
+ clientSslContextFactory.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_keystore.p12").getAbsolutePath());
clientSslContextFactory.setKeyStorePassword("storepwd");
clientSslContextFactory.setEndpointIdentificationAlgorithm(null);
ClientConnector clientConnector = new ClientConnector();
@@ -747,7 +747,7 @@ public class ForwardProxyTLSServerTest
return super.newSSLEngine(host, port);
}
};
- clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_server_keystore.p12").getAbsolutePath());
+ clientTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_server_keystore.p12").getAbsolutePath());
clientTLS.setKeyStorePassword("storepwd");
clientTLS.setEndpointIdentificationAlgorithm(null);
ClientConnector clientConnector = new ClientConnector();
@@ -765,7 +765,7 @@ public class ForwardProxyTLSServerTest
return super.newSSLEngine(host, port);
}
};
- proxyClientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_proxy_keystore.p12").getAbsolutePath());
+ proxyClientTLS.setKeyStorePath(MavenTestingUtils.getTargetFile("test-classes/client_proxy_keystore.p12").getAbsolutePath());
proxyClientTLS.setKeyStorePassword("storepwd");
proxyClientTLS.setEndpointIdentificationAlgorithm(null);
proxyClientTLS.start();
diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java
index 21f7f859fa3..ac731694235 100644
--- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java
+++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java
@@ -140,7 +140,7 @@ public class ProxyServletTest
server.addConnector(serverConnector);
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
- String keyStorePath = MavenTestingUtils.getTestResourceFile("server_keystore.p12").getAbsolutePath();
+ String keyStorePath = MavenTestingUtils.getTargetFile("test-classes/server_keystore.p12").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
tlsServerConnector = new ServerConnector(server, new SslConnectionFactory(
From 02324c35e2e3aea4ed62d92e8e3770f0607ef265 Mon Sep 17 00:00:00 2001
From: Olivier Lamy
Date: Mon, 25 Jul 2022 19:57:44 +1000
Subject: [PATCH 2/4] remove redundant option
Signed-off-by: Olivier Lamy
---
.github/workflows/codeql-analysis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 46013d167c4..43b384206f8 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -69,7 +69,7 @@ jobs:
- name: Set up Maven
run:
- mvn -e -B -V --show-version org.apache.maven.plugins:maven-wrapper-plugin:3.1.0:wrapper "-Dmaven=3.8.6"
+ mvn -e -B -V org.apache.maven.plugins:maven-wrapper-plugin:3.1.0:wrapper "-Dmaven=3.8.6"
- name: Clean install dependencies and build
run: ./mvnw clean install -DskipTests -V -B
From 69a7b06f065378dab1d53371e0f2f71a74b6e20e Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 25 Jul 2022 13:22:14 +0200
Subject: [PATCH 3/4] Jetty 12.0.x retainable chunk (#8325)
Made Chunk implement Retainable, and protocol implementations
to link the RetainableByteBuffer all the way to the Chunk.
Extended Retainable to have both a retain() and a release() methods.
Removed HTTP/2's ISession and IStream, now just using HTTP2Session and HTTP2Stream.
Removed *.Adapter classes in favor of interfaces with default methods.
Javadoc additions and clarifications.
Signed-off-by: Simone Bordet
---
.../asciidoc/programming-guide/http2.adoc | 43 +-
.../server/http2/server-http2.adoc | 2 +-
.../jetty/docs/programming/HTTP2Docs.java | 41 +-
.../client/http2/HTTP2ClientDocs.java | 84 ++--
.../client/http3/HTTP3ClientDocs.java | 10 +-
.../server/http2/HTTP2ServerDocs.java | 80 +++-
.../server/http3/HTTP3ServerDocs.java | 10 +-
.../java/client/ConscryptHTTP2ClientTest.java | 17 +-
.../alpn/java/client/JDK9HTTP2ClientTest.java | 17 +-
.../eclipse/jetty/client/HttpReceiver.java | 19 +-
.../util/InputStreamRequestContent.java | 8 +-
.../jetty/client/util/PathRequestContent.java | 18 +-
.../server/internal/ServerFCGIConnection.java | 2 +-
.../java/org/eclipse/jetty/http/Trailers.java | 17 +-
.../jetty/http2/client/HTTP2Client.java | 6 +-
.../client/HTTP2ClientConnectionFactory.java | 9 +-
.../client/internal/HTTP2ClientSession.java | 10 +-
.../http2/AbstractFlowControlStrategy.java | 83 ++--
.../http2/BufferingFlowControlStrategy.java | 28 +-
.../jetty/http2/FlowControlStrategy.java | 20 +-
.../org/eclipse/jetty/http2/ISession.java | 169 --------
.../java/org/eclipse/jetty/http2/IStream.java | 228 ----------
.../http2/SimpleFlowControlStrategy.java | 14 +-
.../org/eclipse/jetty/http2/api/Session.java | 125 ++----
.../org/eclipse/jetty/http2/api/Stream.java | 249 +++++++----
.../api/server/ServerSessionListener.java | 11 +-
.../jetty/http2/internal/HTTP2Channel.java | 5 +-
.../jetty/http2/internal/HTTP2Connection.java | 92 ++--
.../jetty/http2/internal/HTTP2Flusher.java | 29 +-
.../jetty/http2/internal/HTTP2Session.java | 382 ++++++++---------
.../jetty/http2/internal/HTTP2Stream.java | 399 ++++++++----------
.../http2/internal/HTTP2StreamEndPoint.java | 212 +++-------
.../internal/ClientHTTP2StreamEndPoint.java | 9 +-
.../internal/HTTPSessionListenerPromise.java | 31 +-
.../http/internal/HttpChannelOverHTTP2.java | 20 +-
.../http/internal/HttpReceiverOverHTTP2.java | 381 +++++------------
.../http/internal/HttpSenderOverHTTP2.java | 8 +-
.../AbstractHTTP2ServerConnectionFactory.java | 10 +-
.../server/HTTP2ServerConnectionFactory.java | 30 +-
.../RawHTTP2ServerConnectionFactory.java | 9 +-
.../internal/HTTP2ServerConnection.java | 45 +-
.../server/internal/HTTP2ServerSession.java | 19 +-
.../server/internal/HttpStreamOverHTTP2.java | 38 +-
.../internal/HttpTransportOverHTTP2.java | 2 +-
.../internal/ServerHTTP2StreamEndPoint.java | 13 +-
.../jetty-http2/jetty-http2-tests/pom.xml | 3 +-
.../jetty/http2/tests/AsyncIOTest.java | 16 +-
.../jetty/http2/tests/AsyncServletTest.java | 20 +-
.../BlockedWritesWithSmallThreadPoolTest.java | 33 +-
.../eclipse/jetty/http2/tests/CloseTest.java | 6 +-
.../tests/ConcurrentStreamCreationTest.java | 6 +-
.../jetty/http2/tests/ConnectTimeoutTest.java | 4 +-
.../jetty/http2/tests/ConnectTunnelTest.java | 43 +-
.../jetty/http2/tests/ContentLengthTest.java | 40 ++
.../jetty/http2/tests/DataDemandTest.java | 189 ++++-----
.../http2/tests/FlowControlStalledTest.java | 76 ++--
.../http2/tests/FlowControlStrategyTest.java | 310 ++++++++------
.../http2/tests/FlowControlWindowsTest.java | 34 +-
.../eclipse/jetty/http2/tests/GoAwayTest.java | 201 +++++----
.../jetty/http2/tests/H2SpecServer.java | 19 +
.../jetty/http2/tests/HTTP2ServerTest.java | 2 +-
.../eclipse/jetty/http2/tests/HTTP2Test.java | 214 +++++-----
.../HttpClientTransportOverHTTP2Test.java | 100 ++++-
.../jetty/http2/tests/IdleTimeoutTest.java | 157 +++----
.../jetty/http2/tests/InterleavingTest.java | 41 +-
.../http2/tests/MaxConcurrentStreamsTest.java | 15 +-
.../http2/tests/MaxPushedStreamsTest.java | 13 +-
.../eclipse/jetty/http2/tests/PingTest.java | 4 +-
.../jetty/http2/tests/PrefaceTest.java | 34 +-
.../tests/PriorKnowledgeHTTP2OverTLSTest.java | 4 +-
.../jetty/http2/tests/PriorityTest.java | 18 +-
.../jetty/http2/tests/ProxyProtocolTest.java | 8 +-
.../eclipse/jetty/http2/tests/ProxyTest.java | 8 +-
.../http2/tests/PushCacheFilterTest.java | 76 ++--
.../http2/tests/PushedResourcesTest.java | 7 +-
.../jetty/http2/tests/RawHTTP2ProxyTest.java | 88 ++--
.../http2/tests/RequestTrailersTest.java | 30 +-
.../http2/tests/ResponseTrailerTest.java | 13 +-
.../jetty/http2/tests/SessionFailureTest.java | 15 +-
.../http2/tests/SmallThreadPoolLoadTest.java | 26 +-
.../jetty/http2/tests/StreamCloseTest.java | 112 ++---
.../jetty/http2/tests/StreamCountTest.java | 50 ++-
.../jetty/http2/tests/StreamResetTest.java | 159 +++----
.../jetty/http2/tests/TrailersTest.java | 45 +-
.../test/resources/jetty-logging.properties | 1 +
.../org/eclipse/jetty/http3/api/Stream.java | 30 +-
.../http3/internal/HTTP3StreamConnection.java | 32 +-
.../http/internal/HttpReceiverOverHTTP3.java | 67 ++-
.../server/internal/HttpStreamOverHTTP3.java | 17 +-
.../jetty/http3/tests/ClientServerTest.java | 8 +-
.../jetty/http3/tests/DataDemandTest.java | 4 +-
.../jetty/http3/tests/ExternalServerTest.java | 2 +-
.../eclipse/jetty/http3/tests/GoAwayTest.java | 6 +-
.../http3/tests/HandlerClientServerTest.java | 2 +-
.../java/org/eclipse/jetty/io/Content.java | 106 +++--
.../java/org/eclipse/jetty/io/Retainable.java | 146 +++++++
.../jetty/io/RetainableByteBuffer.java | 55 +--
.../io/content/ContentSourceTransformer.java | 57 +--
.../io/content/InputStreamContentSource.java | 18 +-
.../jetty/io/content/PathContentSource.java | 40 +-
.../jetty/io/internal/ByteBufferChunk.java | 92 +++-
.../eclipse/jetty/io/ContentSourceTest.java | 5 +-
.../io/ContentSourceTransformerTest.java | 3 +-
.../jetty/server/handler/DelayedHandler.java | 2 +
.../server/handler/gzip/GzipRequest.java | 72 ++--
.../jetty/server/internal/HttpConnection.java | 29 +-
.../org/eclipse/jetty/util/Retainable.java | 19 -
.../jetty/ee10/servlet/ServletChannel.java | 1 +
.../client/ProxyWithDynamicTransportTest.java | 8 +-
.../jetty/test/webapp/HTTP1Servlet.java | 4 +-
.../ee9/nested/AsyncContentProducerTest.java | 12 +-
.../nested/BlockingContentProducerTest.java | 10 +-
.../client/ProxyWithDynamicTransportTest.java | 8 +-
113 files changed, 3039 insertions(+), 3110 deletions(-)
delete mode 100644 jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
delete mode 100644 jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java
create mode 100644 jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/Retainable.java
delete mode 100644 jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Retainable.java
diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/http2.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/http2.adoc
index 0b819a53d6f..aff3ee71df1 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/http2.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/http2.adoc
@@ -26,9 +26,11 @@ This means that a sender and a receiver maintain a _flow control window_ that tr
When a sender sends data bytes, it reduces its flow control window.
When a receiver receives data bytes, it also reduces its flow control window, and then passes the received data bytes to the application.
The application consumes the data bytes and tells back the receiver that it has consumed the data bytes.
-The receiver then enlarges the flow control window, and arranges to send a message to the sender with the number of bytes consumed, so that the sender can enlarge its flow control window.
+The receiver then enlarges the flow control window, and the implementation arranges to send a message to the sender with the number of bytes consumed, so that the sender can enlarge its flow control window.
-A sender can send data bytes up to its whole flow control window, then it must stop sending until it receives a message from the receiver that the data bytes have been consumed, which enlarges the flow control window, which allows the sender to send more data bytes.
+A sender can send data bytes up to its whole flow control window, then it must stop sending.
+The sender may resume sending data bytes when it receives a message from the receiver that the data bytes sent previously have been consumed.
+This message enlarges the sender flow control window, which allows the sender to send more data bytes.
HTTP/2 defines _two_ flow control windows: one for each _session_, and one for each _stream_.
Let's see with an example how they interact, assuming that in this example the session flow control window is 120 bytes and the stream flow control window is 100 bytes.
@@ -37,33 +39,46 @@ The sender opens a session, and then opens `stream_1` on that session, and sends
At this point the session flow control window is `40` bytes (`120 - 80`), and ``stream_1``'s flow control window is `20` bytes (`100 - 80`).
The sender now opens `stream_2` on the same session and sends `40` data bytes.
At this point, the session flow control window is `0` bytes (`40 - 40`), while ``stream_2``'s flow control window is `60` (`100 - 40`).
-Since now the session flow control window is `0`, the sender cannot send more data bytes, neither on `stream_1` nor on `stream_2` despite both have their stream flow control windows greater than `0`.
+Since now the session flow control window is `0`, the sender cannot send more data bytes, neither on `stream_1` nor on `stream_2`, nor on other streams, despite all the streams having their stream flow control windows greater than `0`.
The receiver consumes ``stream_2``'s `40` data bytes and sends a message to the sender with this information.
-At this point, the session flow control window is `40` (`0 40`), ``stream_1``'s flow control window is still `20` and ``stream_2``'s flow control window is `100` (`60 40`).
-If the sender opens `stream_3` and would like to send 50 data bytes, it would only be able to send `40` because that is the maximum allowed by the session flow control window at this point.
+At this point, the session flow control window is `40` (``0 + 40``), ``stream_1``'s flow control window is still `20` and ``stream_2``'s flow control window is `100` (``60 + 40``).
+If the sender opens `stream_3` and would like to send `50` data bytes, it would only be able to send `40` because that is the maximum allowed by the session flow control window at this point.
It is therefore very important that applications notify the fact that they have consumed data bytes as soon as possible, so that the implementation (the receiver) can send a message to the sender (in the form of a `WINDOW_UPDATE` frame) with the information to enlarge the flow control window, therefore reducing the possibility that sender stalls due to the flow control windows being reduced to `0`.
end::flowControl[]
tag::apiFlowControl[]
-NOTE: Returning from the `onData(...)` method implicitly demands for more `DATA` frames (unless the one just delivered was the last).
-Additional `DATA` frames may be delivered immediately if they are available or later, asynchronously, when they arrive.
+[NOTE]
+====
+When `onDataAvailable(Stream stream)` is invoked, the demand is implicitly cancelled.
-Applications that consume the content buffer within `onData(...)` (for example, writing it to a file, or copying the bytes to another storage) should succeed the callback as soon as they have consumed the content buffer.
+Just returning from the `onDataAvailable(Stream stream)` method does _not_ implicitly demand for more `DATA` frames.
+
+Applications must call `Stream.demand()` to explicitly require that `onDataAvailable(Stream stream)` is invoked again when more `DATA` frames are available.
+====
+
+Applications that consume the content buffer within `onDataAvailable(Stream stream)` (for example, writing it to a file, or copying the bytes to another storage) should call `Data.release()` as soon as they have consumed the content buffer.
This allows the implementation to reuse the buffer, reducing the memory requirements needed to handle the content buffers.
-Alternatively, a client application may store away _both_ the buffer and the callback to consume the buffer bytes later, or pass _both_ the buffer and the callback to another asynchronous API (this is typical in proxy applications).
+Alternatively, a client application may store away the `Data` object to consume the buffer bytes later, or pass the `Data` object to another asynchronous API (this is typical in proxy applications).
-IMPORTANT: Completing the `Callback` is very important not only to allow the implementation to reuse the buffer, but also tells the implementation to enlarge the stream and session flow control windows so that the sender will be able to send more `DATA` frames without stalling.
+[IMPORTANT]
+====
+Calling `Data.release()` is very important not only to allow the implementation to reuse the buffer, but also tells the implementation to enlarge the stream and session flow control windows so that the sender will be able to send more `DATA` frames without stalling.
+====
-Applications can also precisely control _when_ to demand more `DATA` frames, by implementing the `onDataDemanded(...)` method instead of `onData(...)`:
+Applications can unwrap the `Data` object into some other object that may be used later, provided that the _release_ semantic is maintained:
[source,java,indent=0]
----
-include::{doc_code}/org/eclipse/jetty/docs/programming/HTTP2Docs.java[tags=dataDemanded]
+include::{doc_code}/org/eclipse/jetty/docs/programming/HTTP2Docs.java[tags=dataUnwrap]
----
-IMPORTANT: Applications that implement `onDataDemanded(...)` must remember to call `Stream.demand(...)`.
-If they don't, the implementation will not deliver `DATA` frames and the application will stall threadlessly until an idle timeout fires to close the stream or the session.
+[IMPORTANT]
+====
+Applications that implement `onDataAvailable(Stream stream)` must remember to call `Stream.demand()` eventually.
+
+If they do not call `Stream.demand()`, the implementation will not invoke `onDataAvailable(Stream stream)` to deliver more `DATA` frames and the application will stall threadlessly until an idle timeout fires to close the stream or the session.
+====
end::apiFlowControl[]
diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/http2/server-http2.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/http2/server-http2.adoc
index 07afeaa2b20..6c2f8e146b3 100644
--- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/http2/server-http2.adoc
+++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/http2/server-http2.adoc
@@ -89,7 +89,7 @@ Receiving the `HEADERS` frame opens the `Stream`:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java[tags=request]
----
-Server applications should return a `Stream.Listener` implementation from `onNewStream(...)` to be notified of events generated by the client, such as `DATA` frames carrying request content, or a `RST_STREAM` frame indicating that the client wants to _reset_ the request, or an idle timeout event indicating that the client was supposed to send more frames but it did not.
+Server applications should return a `Stream.Listener` implementation from `onNewStream(\...)` to be notified of events generated by the client, such as `DATA` frames carrying request content, or a `RST_STREAM` frame indicating that the client wants to _reset_ the request, or an idle timeout event indicating that the client was supposed to send more frames but it did not.
The example below shows how to receive request content:
diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java
index 80386ea2f4e..0c5b57f5020 100644
--- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java
@@ -28,7 +28,6 @@ import org.eclipse.jetty.http.MetaData;
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.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
@@ -40,7 +39,7 @@ public class HTTP2Docs
HTTP2Client http2Client = new HTTP2Client();
http2Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -48,7 +47,7 @@ public class HTTP2Docs
MetaData.Request request = new MetaData.Request("GET", HttpURI.from("http://localhost:8080/path"), HttpVersion.HTTP_2, requestHeaders);
HeadersFrame headersFrame = new HeadersFrame(request, null, true);
- // tag::dataDemanded[]
+ // tag::dataUnwrap[]
class Chunk
{
private final ByteBuffer buffer;
@@ -64,29 +63,39 @@ public class HTTP2Docs
// A queue that consumers poll to consume content asynchronously.
Queue dataQueue = new ConcurrentLinkedQueue<>();
- // Implementation of Stream.Listener.onDataDemanded(...)
- // in case of asynchronous content consumption and demand.
- Stream.Listener listener = new Stream.Listener.Adapter()
+ // Implementation of Stream.Listener.onDataAvailable(Stream stream)
+ // in case of unwrapping of the Data object for asynchronous content
+ // consumption and demand.
+ Stream.Listener listener = new Stream.Listener()
{
@Override
- public void onDataDemanded(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
- // Get the content buffer.
- ByteBuffer buffer = frame.getData();
+ Stream.Data data = stream.readData();
- // Store buffer to consume it asynchronously, and wrap the callback.
+ if (data == null)
+ {
+ stream.demand();
+ return;
+ }
+
+ // Get the content buffer.
+ ByteBuffer buffer = data.frame().getData();
+
+ // Unwrap the Data object, converting it to a Chunk.
+ // The Data.release() semantic is maintained in the completion of the Callback.
dataQueue.offer(new Chunk(buffer, Callback.from(() ->
{
// When the buffer has been consumed, then:
- // A) succeed the nested callback.
- callback.succeeded();
+ // A) release the Data object.
+ data.release();
// B) demand more DATA frames.
- stream.demand(1);
- }, callback::failed)));
+ stream.demand();
+ })));
- // Do not demand more content here, to avoid to overflow the queue.
+ // Do not demand more data here, to avoid to overflow the queue.
}
};
- // end::dataDemanded[]
+ // end::dataUnwrap[]
}
}
diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java
index dfbf13d2e25..4f8ec17f66f 100644
--- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java
@@ -77,7 +77,7 @@ public class HTTP2ClientDocs
// Connect to the server, the CompletableFuture will be
// notified when the connection is succeeded (or failed).
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
// Block to obtain the Session.
// Alternatively you can use the CompletableFuture APIs to avoid blocking.
@@ -98,7 +98,7 @@ public class HTTP2ClientDocs
// Connect to the server, the CompletableFuture will be
// notified when the connection is succeeded (or failed).
- CompletableFuture sessionCF = http2Client.connect(connector.getSslContextFactory(), serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(connector.getSslContextFactory(), serverAddress, new Session.Listener() {});
// Block to obtain the Session.
// Alternatively you can use the CompletableFuture APIs to avoid blocking.
@@ -113,7 +113,7 @@ public class HTTP2ClientDocs
// tag::configure[]
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- http2Client.connect(serverAddress, new Session.Listener.Adapter()
+ http2Client.connect(serverAddress, new Session.Listener()
{
@Override
public Map onPreface(Session session)
@@ -138,7 +138,7 @@ public class HTTP2ClientDocs
http2Client.start();
// tag::newStream[]
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
// Configure the request headers.
@@ -153,7 +153,7 @@ public class HTTP2ClientDocs
HeadersFrame headersFrame = new HeadersFrame(request, null, true);
// Open a Stream by sending the HEADERS frame.
- session.newStream(headersFrame, new Stream.Listener.Adapter());
+ session.newStream(headersFrame, null);
// end::newStream[]
}
@@ -163,7 +163,7 @@ public class HTTP2ClientDocs
http2Client.start();
// tag::newStreamWithData[]
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
// Configure the request headers.
@@ -178,7 +178,7 @@ public class HTTP2ClientDocs
HeadersFrame headersFrame = new HeadersFrame(request, null, false);
// Open a Stream by sending the HEADERS frame.
- CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener.Adapter());
+ CompletableFuture streamCF = session.newStream(headersFrame, null);
// Block to obtain the Stream.
// Alternatively you can use the CompletableFuture APIs to avoid blocking.
@@ -205,7 +205,7 @@ public class HTTP2ClientDocs
HTTP2Client http2Client = new HTTP2Client();
http2Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -215,7 +215,7 @@ public class HTTP2ClientDocs
// tag::responseListener[]
// Open a Stream by sending the HEADERS frame.
- session.newStream(headersFrame, new Stream.Listener.Adapter()
+ session.newStream(headersFrame, new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
@@ -227,6 +227,12 @@ public class HTTP2ClientDocs
{
MetaData.Response response = (MetaData.Response)metaData;
System.getLogger("http2").log(INFO, "Received response {0}", response);
+ if (!frame.isEndStream())
+ {
+ // Demand for DATA frames, so that onDataAvailable()
+ // below will be called when they are available.
+ stream.demand();
+ }
}
else
{
@@ -235,19 +241,29 @@ public class HTTP2ClientDocs
}
@Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
+ // Read a Data object.
+ Stream.Data data = stream.readData();
+
+ if (data == null)
+ {
+ // Demand more DATA frames.
+ stream.demand();
+ return;
+ }
+
// Get the content buffer.
- ByteBuffer buffer = frame.getData();
+ ByteBuffer buffer = data.frame().getData();
// Consume the buffer, here - as an example - just log it.
System.getLogger("http2").log(INFO, "Consuming buffer {0}", buffer);
// Tell the implementation that the buffer has been consumed.
- callback.succeeded();
+ data.release();
- // By returning from the method, implicitly tell the implementation
- // to deliver to this method more DATA frames when they are available.
+ // Demand more DATA frames when they are available.
+ stream.demand();
}
});
// end::responseListener[]
@@ -258,7 +274,7 @@ public class HTTP2ClientDocs
HTTP2Client http2Client = new HTTP2Client();
http2Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -268,12 +284,15 @@ public class HTTP2ClientDocs
// tag::reset[]
// Open a Stream by sending the HEADERS frame.
- CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener.Adapter()
+ CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener()
{
@Override
- public void onReset(Stream stream, ResetFrame frame)
+ public void onReset(Stream stream, ResetFrame frame, Callback callback)
{
// The server reset this stream.
+
+ // Succeed the callback to signal that the reset event has been handled.
+ callback.succeeded();
}
});
Stream stream = streamCF.get();
@@ -288,7 +307,7 @@ public class HTTP2ClientDocs
HTTP2Client http2Client = new HTTP2Client();
http2Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -298,7 +317,7 @@ public class HTTP2ClientDocs
// tag::push[]
// Open a Stream by sending the HEADERS frame.
- CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener.Adapter()
+ CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener()
{
@Override
public Stream.Listener onPush(Stream pushedStream, PushPromiseFrame frame)
@@ -314,7 +333,7 @@ public class HTTP2ClientDocs
Stream primaryStream = pushedStream.getSession().getStream(frame.getStreamId());
// Return a Stream.Listener to listen for the pushed "response" events.
- return new Stream.Listener.Adapter()
+ return new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
@@ -326,18 +345,29 @@ public class HTTP2ClientDocs
{
// The pushed "response" headers.
HttpFields pushedResponseHeaders = metaData.getFields();
+
+ // Typically a pushed stream has data, so demand for data.
+ stream.demand();
}
}
@Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
// Handle the pushed stream "response" content.
+ Stream.Data data = stream.readData();
+
+ if (data == null)
+ {
+ stream.demand();
+ return;
+ }
+
// The pushed stream "response" content bytes.
- ByteBuffer buffer = frame.getData();
- // Consume the buffer and complete the callback.
- callback.succeeded();
+ ByteBuffer buffer = data.frame().getData();
+ // Consume the buffer and release the Data object.
+ data.release();
}
};
}
@@ -350,7 +380,7 @@ public class HTTP2ClientDocs
HTTP2Client http2Client = new HTTP2Client();
http2Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener() {});
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -360,12 +390,12 @@ public class HTTP2ClientDocs
// tag::pushReset[]
// Open a Stream by sending the HEADERS frame.
- CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener.Adapter()
+ CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener()
{
@Override
public Stream.Listener onPush(Stream pushedStream, PushPromiseFrame frame)
{
- // Reset the pushed stream to tell the server we are not interested.
+ // Reset the pushed stream to tell the server you are not interested.
pushedStream.reset(new ResetFrame(pushedStream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
// Not interested in listening to pushed response events.
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 e54d5bf5da3..66229040988 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
@@ -212,7 +212,7 @@ public class HTTP3ClientDocs
process(data.getByteBuffer());
// Notify the implementation that the content has been consumed.
- data.complete();
+ data.release();
if (!data.isLast())
{
@@ -266,7 +266,7 @@ public class HTTP3ClientDocs
HTTP3Client http3Client = new HTTP3Client();
http3Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8444);
- CompletableFuture sessionCF = http3Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http3Client.connect(serverAddress, new Session.Listener());
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -276,7 +276,7 @@ public class HTTP3ClientDocs
// tag::push[]
// Open a Stream by sending the HEADERS frame.
- CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener.Adapter()
+ CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener()
{
@Override
public Stream.Listener onPush(Stream pushedStream, PushPromiseFrame frame)
@@ -328,7 +328,7 @@ public class HTTP3ClientDocs
HTTP3Client http3Client = new HTTP3Client();
http3Client.start();
SocketAddress serverAddress = new InetSocketAddress("localhost", 8444);
- CompletableFuture sessionCF = http3Client.connect(serverAddress, new Session.Listener.Adapter());
+ CompletableFuture sessionCF = http3Client.connect(serverAddress, new Session.Listener());
Session session = sessionCF.get();
HttpFields requestHeaders = HttpFields.build()
@@ -338,7 +338,7 @@ public class HTTP3ClientDocs
// tag::pushReset[]
// Open a Stream by sending the HEADERS frame.
- CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener.Adapter()
+ CompletableFuture streamCF = session.newStream(headersFrame, new Stream.Listener()
{
@Override
public Stream.Listener onPush(Stream pushedStream, PushPromiseFrame frame)
diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java
index a3dac9ec95c..94753853aa7 100644
--- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http2/HTTP2ServerDocs.java
@@ -51,7 +51,7 @@ public class HTTP2ServerDocs
// Create a Server instance.
Server server = new Server();
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter();
+ ServerSessionListener sessionListener = new ServerSessionListener() {};
// Create a ServerConnector with RawHTTP2ServerConnectionFactory.
RawHTTP2ServerConnectionFactory http2 = new RawHTTP2ServerConnectionFactory(sessionListener);
@@ -77,7 +77,7 @@ public class HTTP2ServerDocs
public void accept()
{
// tag::accept[]
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
@Override
public void onAccept(Session session)
@@ -92,7 +92,7 @@ public class HTTP2ServerDocs
public void preface()
{
// tag::preface[]
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
@Override
public Map onPreface(Session session)
@@ -112,7 +112,7 @@ public class HTTP2ServerDocs
public void request()
{
// tag::request[]
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
@@ -122,7 +122,10 @@ public class HTTP2ServerDocs
// Return a Stream.Listener to handle the request events,
// for example request content events or a request reset.
- return new Stream.Listener.Adapter();
+ return new Stream.Listener()
+ {
+ // Override callback methods for events you are interested in.
+ };
}
};
// end::request[]
@@ -131,29 +134,41 @@ public class HTTP2ServerDocs
public void requestContent()
{
// tag::requestContent[]
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
MetaData.Request request = (MetaData.Request)frame.getMetaData();
+
+ // Demand for request data content.
+ stream.demand();
+
// Return a Stream.Listener to handle the request events.
- return new Stream.Listener.Adapter()
+ return new Stream.Listener()
{
@Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
+ Stream.Data data = stream.readData();
+
+ if (data == null)
+ {
+ stream.demand();
+ return;
+ }
+
// Get the content buffer.
- ByteBuffer buffer = frame.getData();
+ ByteBuffer buffer = data.frame().getData();
// Consume the buffer, here - as an example - just log it.
System.getLogger("http2").log(INFO, "Consuming buffer {0}", buffer);
// Tell the implementation that the buffer has been consumed.
- callback.succeeded();
+ data.release();
- // By returning from the method, implicitly tell the implementation
- // to deliver to this method more DATA frames when they are available.
+ // Demand more DATA frames when they are available.
+ stream.demand();
}
};
}
@@ -164,7 +179,7 @@ public class HTTP2ServerDocs
public void response()
{
// tag::response[]
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
@@ -178,15 +193,30 @@ public class HTTP2ServerDocs
}
else
{
- return new Stream.Listener.Adapter()
+ // Demand for request data.
+ stream.demand();
+
+ // Return a listener to handle the request events.
+ return new Stream.Listener()
{
@Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
+ Stream.Data data = stream.readData();
+
+ if (data == null)
+ {
+ stream.demand();
+ return;
+ }
+
// Consume the request content.
- callback.succeeded();
- if (frame.isEndStream())
+ data.release();
+
+ if (data.frame().isEndStream())
respond(stream, request);
+ else
+ stream.demand();
}
};
}
@@ -230,7 +260,7 @@ public class HTTP2ServerDocs
{
float maxRequestRate = 0F;
// tag::reset[]
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
@@ -247,7 +277,10 @@ public class HTTP2ServerDocs
// The request is accepted.
MetaData.Request request = (MetaData.Request)frame.getMetaData();
// Return a Stream.Listener to handle the request events.
- return new Stream.Listener.Adapter();
+ return new Stream.Listener()
+ {
+ // Override callback methods for events you are interested in.
+ };
}
}
// tag::exclude[]
@@ -267,7 +300,7 @@ public class HTTP2ServerDocs
// The favicon bytes.
ByteBuffer faviconBuffer = BufferUtil.toBuffer(Resource.newResource("/path/to/favicon.ico"), true);
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
// By default, push is enabled.
private boolean pushEnabled = true;
@@ -292,7 +325,7 @@ public class HTTP2ServerDocs
HttpURI pushedURI = HttpURI.build(request.getURI()).path("/favicon.ico");
MetaData.Request pushedRequest = new MetaData.Request("GET", pushedURI, HttpVersion.HTTP_2, HttpFields.EMPTY);
PushPromiseFrame promiseFrame = new PushPromiseFrame(stream.getId(), 0, pushedRequest);
- stream.push(promiseFrame, new Stream.Listener.Adapter())
+ stream.push(promiseFrame, null)
.thenCompose(pushedStream ->
{
// Send the favicon "response".
@@ -302,7 +335,10 @@ public class HTTP2ServerDocs
});
}
// Return a Stream.Listener to handle the request events.
- return new Stream.Listener.Adapter();
+ return new Stream.Listener()
+ {
+ // Override callback methods for events you are interested in.
+ };
}
};
// end::push[]
diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http3/HTTP3ServerDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http3/HTTP3ServerDocs.java
index 588b4affd78..960a7f2561b 100644
--- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http3/HTTP3ServerDocs.java
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/http3/HTTP3ServerDocs.java
@@ -157,7 +157,7 @@ public class HTTP3ServerDocs
System.getLogger("http3").log(INFO, "Consuming buffer {0}", buffer);
// Tell the implementation that the buffer has been consumed.
- data.complete();
+ data.release();
if (!data.isLast())
{
@@ -204,7 +204,7 @@ public class HTTP3ServerDocs
else
{
// Consume the request content.
- data.complete();
+ data.release();
if (data.isLast())
respond(stream, request);
}
@@ -290,7 +290,7 @@ public class HTTP3ServerDocs
// The favicon bytes.
ByteBuffer faviconBuffer = BufferUtil.toBuffer(Resource.newResource("/path/to/favicon.ico"), true);
- ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ ServerSessionListener sessionListener = new ServerSessionListener()
{
// By default, push is enabled.
private boolean pushEnabled = true;
@@ -315,7 +315,7 @@ public class HTTP3ServerDocs
HttpURI pushedURI = HttpURI.build(request.getURI()).path("/favicon.ico");
MetaData.Request pushedRequest = new MetaData.Request("GET", pushedURI, HttpVersion.HTTP_2, HttpFields.EMPTY);
PushPromiseFrame promiseFrame = new PushPromiseFrame(stream.getId(), 0, pushedRequest);
- stream.push(promiseFrame, new Stream.Listener.Adapter())
+ stream.push(promiseFrame, null)
.thenCompose(pushedStream ->
{
// Send the favicon "response".
@@ -325,7 +325,7 @@ public class HTTP3ServerDocs
});
}
// Return a Stream.Listener to handle the request events.
- return new Stream.Listener.Adapter();
+ return new Stream.Listener();
}
};
// end::push[]
diff --git a/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2ClientTest.java b/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2ClientTest.java
index ce12386b3b4..2acbb0983db 100644
--- a/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2ClientTest.java
+++ b/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2ClientTest.java
@@ -28,9 +28,7 @@ import org.eclipse.jetty.http.MetaData;
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.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
@@ -69,14 +67,14 @@ public class ConscryptHTTP2ClientTest
client.start();
FuturePromise sessionPromise = new FuturePromise<>();
- client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
+ client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener() {}, sessionPromise);
Session session = sessionPromise.get(15, TimeUnit.SECONDS);
HttpFields requestFields = HttpFields.build().put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
CountDownLatch latch = new CountDownLatch(1);
- session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+ session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
@@ -84,14 +82,17 @@ public class ConscryptHTTP2ClientTest
System.err.println(frame);
if (frame.isEndStream())
latch.countDown();
+ stream.demand();
}
@Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
- System.err.println(frame);
- callback.succeeded();
- if (frame.isEndStream())
+ Stream.Data data = stream.readData();
+ System.err.println(data);
+ data.release();
+ stream.demand();
+ if (data.frame().isEndStream())
latch.countDown();
}
});
diff --git a/jetty-core/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2ClientTest.java b/jetty-core/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2ClientTest.java
index 63e4e38667a..fb4decbc420 100644
--- a/jetty-core/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2ClientTest.java
+++ b/jetty-core/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2ClientTest.java
@@ -25,9 +25,7 @@ import org.eclipse.jetty.http.MetaData;
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.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
@@ -55,7 +53,7 @@ public class JDK9HTTP2ClientTest
client.start();
FuturePromise sessionPromise = new FuturePromise<>();
- client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
+ client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener() {}, sessionPromise);
Session session = sessionPromise.get(15, TimeUnit.SECONDS);
HttpFields.Mutable requestFields = HttpFields.build();
@@ -63,7 +61,7 @@ public class JDK9HTTP2ClientTest
MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
CountDownLatch latch = new CountDownLatch(1);
- session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+ session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
@@ -71,14 +69,17 @@ public class JDK9HTTP2ClientTest
System.err.println(frame);
if (frame.isEndStream())
latch.countDown();
+ stream.demand();
}
@Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
+ public void onDataAvailable(Stream stream)
{
- System.err.println(frame);
- callback.succeeded();
- if (frame.isEndStream())
+ Stream.Data data = stream.readData();
+ System.err.println(data);
+ data.release();
+ stream.demand();
+ if (data.frame().isEndStream())
latch.countDown();
}
});
diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
index 8315e6b48cd..fd6532ab648 100644
--- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
+++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
@@ -95,7 +95,7 @@ public abstract class HttpReceiver
throw new IllegalArgumentException("Invalid demand " + n);
boolean resume = false;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
demand = MathUtils.cappedAdd(demand, n);
if (stalled)
@@ -104,7 +104,7 @@ public abstract class HttpReceiver
resume = true;
}
if (LOG.isDebugEnabled())
- LOG.debug("Response demand={}/{}, resume={}", n, demand, resume);
+ LOG.debug("Response demand={}/{}, resume={} on {}", n, demand, resume, this);
}
if (resume)
@@ -123,7 +123,7 @@ public abstract class HttpReceiver
private long demand(LongUnaryOperator operator)
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
return demand = operator.applyAsLong(demand);
}
@@ -131,7 +131,7 @@ public abstract class HttpReceiver
protected boolean hasDemandOrStall()
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
stalled = demand <= 0;
return !stalled;
@@ -229,17 +229,11 @@ public abstract class HttpReceiver
{
switch (fieldHeader)
{
- case SET_COOKIE:
- case SET_COOKIE2:
+ case SET_COOKIE, SET_COOKIE2 ->
{
URI uri = exchange.getRequest().getURI();
if (uri != null)
storeCookie(uri, field);
- break;
- }
- default:
- {
- break;
}
}
}
@@ -602,10 +596,11 @@ public abstract class HttpReceiver
@Override
public String toString()
{
- return String.format("%s@%x(rsp=%s,failure=%s)",
+ return String.format("%s@%x(rsp=%s,demand=%d,failure=%s)",
getClass().getSimpleName(),
hashCode(),
responseState,
+ demand(),
failure);
}
diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
index 17b721a27f6..860dad43d8b 100644
--- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
+++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
@@ -16,7 +16,7 @@ package org.eclipse.jetty.client.util;
import java.io.InputStream;
import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.RetainableByteBufferPool;
import org.eclipse.jetty.io.content.InputStreamContentSource;
/**
@@ -33,7 +33,7 @@ public class InputStreamRequestContent extends InputStreamContentSource implemen
public InputStreamRequestContent(InputStream stream)
{
- this("application/octet-stream", stream, null);
+ this(stream, 4096);
}
public InputStreamRequestContent(InputStream stream, int bufferSize)
@@ -43,7 +43,7 @@ public class InputStreamRequestContent extends InputStreamContentSource implemen
public InputStreamRequestContent(String contentType, InputStream stream, int bufferSize)
{
- this(contentType, stream, null);
+ this(contentType, stream);
setBufferSize(bufferSize);
}
@@ -52,7 +52,7 @@ public class InputStreamRequestContent extends InputStreamContentSource implemen
this(contentType, stream, null);
}
- public InputStreamRequestContent(String contentType, InputStream stream, ByteBufferPool bufferPool)
+ public InputStreamRequestContent(String contentType, InputStream stream, RetainableByteBufferPool bufferPool)
{
super(stream, bufferPool);
this.contentType = contentType;
diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
index ae9cb33ede1..35d2dbcdcc4 100644
--- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
+++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
@@ -17,16 +17,15 @@ import java.io.IOException;
import java.nio.file.Path;
import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.RetainableByteBufferPool;
import org.eclipse.jetty.io.content.PathContentSource;
/**
*
A {@link Request.Content} for files using JDK 7's {@code java.nio.file} APIs.
*
It is possible to specify a buffer size used to read content from the stream,
- * by default 4096 bytes, and whether the buffer should be direct or not.
- *
If a {@link ByteBufferPool} is provided via {@link #setByteBufferPool(ByteBufferPool)},
- * the buffer will be allocated from that pool, otherwise one buffer will be
- * allocated and used to read the file.
+ * by default 4096 bytes, whether the buffer should be direct or not, and a
+ * {@link RetainableByteBufferPool} from which {@code ByteBuffer}s will be acquired
+ * to read from the {@code Path}.
*/
public class PathRequestContent extends PathContentSource implements Request.Content
{
@@ -49,11 +48,16 @@ public class PathRequestContent extends PathContentSource implements Request.Con
public PathRequestContent(String contentType, Path filePath, int bufferSize) throws IOException
{
- super(filePath);
- this.contentType = contentType;
+ this(contentType, filePath, null);
setBufferSize(bufferSize);
}
+ public PathRequestContent(String contentType, Path filePath, RetainableByteBufferPool bufferPool) throws IOException
+ {
+ super(filePath, bufferPool);
+ this.contentType = contentType;
+ }
+
@Override
public String getContentType()
{
diff --git a/jetty-core/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java b/jetty-core/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java
index d1cf32f45bd..f1ca41530f0 100644
--- a/jetty-core/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java
+++ b/jetty-core/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/internal/ServerFCGIConnection.java
@@ -372,7 +372,7 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti
if (stream != null)
{
networkBuffer.retain();
- stream.onContent(Content.Chunk.from(buffer, false, networkBuffer::release));
+ stream.onContent(Content.Chunk.from(buffer, false, networkBuffer));
// Signal that the content is processed asynchronously, to ensure backpressure.
return true;
}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Trailers.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Trailers.java
index 9f4d1e61a15..a8acb78d956 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Trailers.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Trailers.java
@@ -39,13 +39,20 @@ public class Trailers implements Content.Chunk
return true;
}
- @Override
- public void release()
- {
- }
-
public HttpFields getTrailers()
{
return trailers;
}
+
+ @Override
+ public void retain()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean release()
+ {
+ return true;
+ }
}
diff --git a/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java b/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
index 93cdebf1286..2eb6d438ff0 100644
--- a/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
+++ b/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
@@ -54,13 +54,13 @@ import org.eclipse.jetty.util.thread.Scheduler;
* int port = 443;
*
* FuturePromise<Session> sessionPromise = new FuturePromise<>();
- * client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);
+ * client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener() {}, sessionPromise);
*
* // Obtain the client Session object.
* Session session = sessionPromise.get(5, TimeUnit.SECONDS);
*
* // Prepare the HTTP request headers.
- * HttpFields requestFields = new HttpFields();
+ * HttpFields requestFields = HttpFields.build();
* requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
* // Prepare the HTTP request object.
* MetaData.Request request = new MetaData.Request("PUT", HttpURI.from("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
@@ -68,7 +68,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* HeadersFrame headersFrame = new HeadersFrame(request, null, false);
*
* // Prepare the listener to receive the HTTP response frames.
- * Stream.Listener responseListener = new new Stream.Listener.Adapter()
+ * Stream.Listener responseListener = new new Stream.Listener()
* {
* @Override
* public void onHeaders(Stream stream, HeadersFrame frame)
diff --git a/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
index fb50fabed5b..3d665b1575f 100644
--- a/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
+++ b/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
@@ -19,13 +19,13 @@ import java.util.Map;
import java.util.concurrent.Executor;
import org.eclipse.jetty.http2.FlowControlStrategy;
-import org.eclipse.jetty.http2.ISession;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.internal.HTTP2ClientSession;
import org.eclipse.jetty.http2.frames.PrefaceFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.http2.internal.HTTP2Connection;
+import org.eclipse.jetty.http2.internal.HTTP2Session;
import org.eclipse.jetty.http2.internal.generator.Generator;
import org.eclipse.jetty.http2.internal.parser.Parser;
import org.eclipse.jetty.io.ByteBufferPool;
@@ -35,7 +35,6 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RetainableByteBufferPool;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.Scheduler;
public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
@@ -85,7 +84,7 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
private final Promise promise;
private final Session.Listener listener;
- private HTTP2ClientConnection(HTTP2Client client, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener)
+ private HTTP2ClientConnection(HTTP2Client client, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, HTTP2Session session, int bufferSize, Promise promise, Session.Listener listener)
{
super(retainableByteBufferPool, executor, endpoint, parser, session, bufferSize);
this.client = client;
@@ -109,7 +108,7 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
PrefaceFrame prefaceFrame = new PrefaceFrame();
SettingsFrame settingsFrame = new SettingsFrame(settings, false);
- ISession session = getSession();
+ HTTP2Session session = getSession();
int windowDelta = client.getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
session.updateRecvWindow(windowDelta);
@@ -150,7 +149,7 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
public void onOpened(Connection connection)
{
HTTP2ClientConnection http2Connection = (HTTP2ClientConnection)connection;
- http2Connection.client.addManaged((LifeCycle)http2Connection.getSession());
+ http2Connection.client.addManaged(http2Connection.getSession());
}
@Override
diff --git a/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/internal/HTTP2ClientSession.java b/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/internal/HTTP2ClientSession.java
index 51da678a07f..525dd58cce2 100644
--- a/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/internal/HTTP2ClientSession.java
+++ b/jetty-core/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/internal/HTTP2ClientSession.java
@@ -16,13 +16,13 @@ package org.eclipse.jetty.http2.client.internal;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.CloseState;
import org.eclipse.jetty.http2.FlowControlStrategy;
-import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
import org.eclipse.jetty.http2.internal.ErrorCode;
import org.eclipse.jetty.http2.internal.HTTP2Session;
+import org.eclipse.jetty.http2.internal.HTTP2Stream;
import org.eclipse.jetty.http2.internal.generator.Generator;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
@@ -47,7 +47,7 @@ public class HTTP2ClientSession extends HTTP2Session
// HEADERS can be received for normal and pushed responses.
int streamId = frame.getStreamId();
- IStream stream = getStream(streamId);
+ HTTP2Stream stream = getStream(streamId);
if (stream != null)
{
MetaData metaData = frame.getMetaData();
@@ -94,7 +94,7 @@ public class HTTP2ClientSession extends HTTP2Session
int streamId = frame.getStreamId();
int pushStreamId = frame.getPromisedStreamId();
- IStream stream = getStream(streamId);
+ HTTP2Stream stream = getStream(streamId);
if (stream == null)
{
if (LOG.isDebugEnabled())
@@ -102,7 +102,7 @@ public class HTTP2ClientSession extends HTTP2Session
}
else
{
- IStream pushStream = createRemoteStream(pushStreamId, frame.getMetaData());
+ HTTP2Stream pushStream = createRemoteStream(pushStreamId, frame.getMetaData());
if (pushStream != null)
{
pushStream.process(frame, Callback.NOOP);
@@ -112,7 +112,7 @@ public class HTTP2ClientSession extends HTTP2Session
}
}
- private Stream.Listener notifyPush(IStream stream, IStream pushStream, PushPromiseFrame frame)
+ private Stream.Listener notifyPush(HTTP2Stream stream, HTTP2Stream pushStream, PushPromiseFrame frame)
{
Stream.Listener listener = stream.getListener();
if (listener == null)
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java
index ccfbe1a3be3..2230fe7415b 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java
@@ -14,13 +14,18 @@
package org.eclipse.jetty.http2;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
+import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
+import org.eclipse.jetty.http2.internal.HTTP2Session;
+import org.eclipse.jetty.http2.internal.HTTP2Stream;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
@@ -35,7 +40,7 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
private final AtomicLong sessionStall = new AtomicLong();
private final AtomicLong sessionStallTime = new AtomicLong();
- private final Map streamsStalls = new ConcurrentHashMap<>();
+ private final Map streamsStalls = new ConcurrentHashMap<>();
private final AtomicLong streamsStallTime = new AtomicLong();
private int initialStreamSendWindow;
private int initialStreamRecvWindow;
@@ -59,20 +64,20 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
}
@Override
- public void onStreamCreated(IStream stream)
+ public void onStreamCreated(Stream stream)
{
- stream.updateSendWindow(initialStreamSendWindow);
- stream.updateRecvWindow(initialStreamRecvWindow);
+ updateSendWindow(stream, getInitialStreamSendWindow());
+ updateRecvWindow(stream, getInitialStreamRecvWindow());
}
@Override
- public void onStreamDestroyed(IStream stream)
+ public void onStreamDestroyed(Stream stream)
{
streamsStalls.remove(stream);
}
@Override
- public void updateInitialStreamWindow(ISession session, int initialStreamWindow, boolean local)
+ public void updateInitialStreamWindow(Session session, int initialStreamWindow, boolean local)
{
int previousInitialStreamWindow;
if (local)
@@ -94,19 +99,19 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
{
if (local)
{
- ((IStream)stream).updateRecvWindow(delta);
+ updateRecvWindow(stream, delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated initial stream recv window {} -> {} for {}", previousInitialStreamWindow, initialStreamWindow, stream);
}
else
{
- session.onWindowUpdate((IStream)stream, new WindowUpdateFrame(stream.getId(), delta));
+ updateWindow(session, stream, new WindowUpdateFrame(stream.getId(), delta));
}
}
}
@Override
- public void onWindowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
+ public void onWindowUpdate(Session session, Stream stream, WindowUpdateFrame frame)
{
int delta = frame.getWindowDelta();
if (frame.getStreamId() > 0)
@@ -114,7 +119,7 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
// The stream may have been removed concurrently.
if (stream != null)
{
- int oldSize = stream.updateSendWindow(delta);
+ int oldSize = updateSendWindow(stream, delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated stream send window {} -> {} for {}", oldSize, oldSize + delta, stream);
if (oldSize <= 0)
@@ -123,7 +128,7 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
}
else
{
- int oldSize = session.updateSendWindow(delta);
+ int oldSize = updateSendWindow(session, delta);
if (LOG.isDebugEnabled())
LOG.debug("Updated session send window {} -> {} for {}", oldSize, oldSize + delta, session);
if (oldSize <= 0)
@@ -132,40 +137,40 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
}
@Override
- public void onDataReceived(ISession session, IStream stream, int length)
+ public void onDataReceived(Session session, Stream stream, int length)
{
- int oldSize = session.updateRecvWindow(-length);
+ int oldSize = updateRecvWindow(session, -length);
if (LOG.isDebugEnabled())
LOG.debug("Data received, {} bytes, updated session recv window {} -> {} for {}", length, oldSize, oldSize - length, session);
if (stream != null)
{
- oldSize = stream.updateRecvWindow(-length);
+ oldSize = updateRecvWindow(stream, -length);
if (LOG.isDebugEnabled())
LOG.debug("Data received, {} bytes, updated stream recv window {} -> {} for {}", length, oldSize, oldSize - length, stream);
}
}
@Override
- public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
+ public void windowUpdate(Session session, Stream stream, WindowUpdateFrame frame)
{
}
@Override
- public void onDataSending(IStream stream, int length)
+ public void onDataSending(Stream stream, int length)
{
if (length == 0)
return;
- ISession session = stream.getSession();
- int oldSessionWindow = session.updateSendWindow(-length);
+ Session session = stream.getSession();
+ int oldSessionWindow = updateSendWindow(session, -length);
int newSessionWindow = oldSessionWindow - length;
if (LOG.isDebugEnabled())
LOG.debug("Sending, session send window {} -> {} for {}", oldSessionWindow, newSessionWindow, session);
if (newSessionWindow <= 0)
onSessionStalled(session);
- int oldStreamWindow = stream.updateSendWindow(-length);
+ int oldStreamWindow = updateSendWindow(stream, -length);
int newStreamWindow = oldStreamWindow - length;
if (LOG.isDebugEnabled())
LOG.debug("Sending, stream send window {} -> {} for {}", oldStreamWindow, newStreamWindow, stream);
@@ -174,25 +179,55 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
}
@Override
- public void onDataSent(IStream stream, int length)
+ public void onDataSent(Stream stream, int length)
{
}
- protected void onSessionStalled(ISession session)
+ protected void updateWindow(Session session, Stream stream, WindowUpdateFrame frame)
+ {
+ ((HTTP2Session)session).onWindowUpdate((HTTP2Stream)stream, frame);
+ }
+
+ protected int updateRecvWindow(Session session, int value)
+ {
+ return ((HTTP2Session)session).updateRecvWindow(value);
+ }
+
+ protected int updateSendWindow(Session session, int value)
+ {
+ return ((HTTP2Session)session).updateSendWindow(value);
+ }
+
+ protected int updateRecvWindow(Stream stream, int value)
+ {
+ return ((HTTP2Stream)stream).updateRecvWindow(value);
+ }
+
+ protected int updateSendWindow(Stream stream, int value)
+ {
+ return ((HTTP2Stream)stream).updateSendWindow(value);
+ }
+
+ protected void sendWindowUpdate(Session session, Stream stream, List frames)
+ {
+ ((HTTP2Session)session).frames((HTTP2Stream)stream, frames, Callback.NOOP);
+ }
+
+ protected void onSessionStalled(Session session)
{
sessionStall.set(System.nanoTime());
if (LOG.isDebugEnabled())
LOG.debug("Session stalled {}", session);
}
- protected void onStreamStalled(IStream stream)
+ protected void onStreamStalled(Stream stream)
{
streamsStalls.put(stream, System.nanoTime());
if (LOG.isDebugEnabled())
LOG.debug("Stream stalled {}", stream);
}
- protected void onSessionUnstalled(ISession session)
+ protected void onSessionUnstalled(Session session)
{
long stallTime = System.nanoTime() - sessionStall.getAndSet(0);
sessionStallTime.addAndGet(stallTime);
@@ -200,7 +235,7 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
LOG.debug("Session unstalled after {} ms {}", TimeUnit.NANOSECONDS.toMillis(stallTime), session);
}
- protected void onStreamUnstalled(IStream stream)
+ protected void onStreamUnstalled(Stream stream)
{
Long time = streamsStalls.remove(stream);
if (time != null)
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java
index 39d105af54e..1c224bf3703 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java
@@ -18,9 +18,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.util.Atomics;
-import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.slf4j.Logger;
@@ -66,7 +67,7 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
private final AtomicInteger maxSessionRecvWindow = new AtomicInteger(DEFAULT_WINDOW_SIZE);
private final AtomicInteger sessionLevel = new AtomicInteger();
- private final Map streamLevels = new ConcurrentHashMap<>();
+ private final Map streamLevels = new ConcurrentHashMap<>();
private float bufferRatio;
public BufferingFlowControlStrategy(float bufferRatio)
@@ -92,21 +93,21 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
}
@Override
- public void onStreamCreated(IStream stream)
+ public void onStreamCreated(Stream stream)
{
super.onStreamCreated(stream);
streamLevels.put(stream, new AtomicInteger());
}
@Override
- public void onStreamDestroyed(IStream stream)
+ public void onStreamDestroyed(Stream stream)
{
streamLevels.remove(stream);
super.onStreamDestroyed(stream);
}
@Override
- public void onDataConsumed(ISession session, IStream stream, int length)
+ public void onDataConsumed(Session session, Stream stream, int length)
{
if (length <= 0)
return;
@@ -119,10 +120,10 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
{
if (sessionLevel.compareAndSet(level, 0))
{
- session.updateRecvWindow(level);
+ updateRecvWindow(session, level);
if (LOG.isDebugEnabled())
LOG.debug("Data consumed, {} bytes, updated session recv window by {}/{} for {}", length, level, maxLevel, session);
- sendWindowUpdate(null, session, new WindowUpdateFrame(0, level));
+ sendWindowUpdate(session, null, List.of(new WindowUpdateFrame(0, level)));
}
else
{
@@ -153,10 +154,10 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
if (level > maxLevel)
{
level = streamLevel.getAndSet(0);
- stream.updateRecvWindow(level);
+ updateRecvWindow(stream, level);
if (LOG.isDebugEnabled())
LOG.debug("Data consumed, {} bytes, updated stream recv window by {}/{} for {}", length, level, maxLevel, stream);
- sendWindowUpdate(stream, session, new WindowUpdateFrame(stream.getId(), level));
+ sendWindowUpdate(session, stream, List.of(new WindowUpdateFrame(stream.getId(), level)));
}
else
{
@@ -168,13 +169,8 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
}
}
- protected void sendWindowUpdate(IStream stream, ISession session, WindowUpdateFrame frame)
- {
- session.frames(stream, List.of(frame), Callback.NOOP);
- }
-
@Override
- public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame)
+ public void windowUpdate(Session session, Stream stream, WindowUpdateFrame frame)
{
super.windowUpdate(session, stream, frame);
@@ -207,7 +203,7 @@ public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
if (frame.getStreamId() == 0)
{
- int sessionWindow = session.updateRecvWindow(0);
+ int sessionWindow = updateRecvWindow(session, 0);
Atomics.updateMax(maxSessionRecvWindow, sessionWindow);
}
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java
index df29a933bef..4502e93bced 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java
@@ -13,29 +13,31 @@
package org.eclipse.jetty.http2;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
public interface FlowControlStrategy
{
public static int DEFAULT_WINDOW_SIZE = 65535;
- public void onStreamCreated(IStream stream);
+ public void onStreamCreated(Stream stream);
- public void onStreamDestroyed(IStream stream);
+ public void onStreamDestroyed(Stream stream);
- public void updateInitialStreamWindow(ISession session, int initialStreamWindow, boolean local);
+ public void updateInitialStreamWindow(Session session, int initialStreamWindow, boolean local);
- public void onWindowUpdate(ISession session, IStream stream, WindowUpdateFrame frame);
+ public void onWindowUpdate(Session session, Stream stream, WindowUpdateFrame frame);
- public void onDataReceived(ISession session, IStream stream, int length);
+ public void onDataReceived(Session session, Stream stream, int length);
- public void onDataConsumed(ISession session, IStream stream, int length);
+ public void onDataConsumed(Session session, Stream stream, int length);
- public void windowUpdate(ISession session, IStream stream, WindowUpdateFrame frame);
+ public void windowUpdate(Session session, Stream stream, WindowUpdateFrame frame);
- public void onDataSending(IStream stream, int length);
+ public void onDataSending(Stream stream, int length);
- public void onDataSent(IStream stream, int length);
+ public void onDataSent(Stream stream, int length);
public interface Factory
{
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
deleted file mode 100644
index 8c957c1016e..00000000000
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
+++ /dev/null
@@ -1,169 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2022 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.http2;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-
-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.Frame;
-import org.eclipse.jetty.http2.frames.PushPromiseFrame;
-import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Promise;
-
-/**
- *
The SPI interface for implementing an HTTP/2 session.
- *
This class extends {@link Session} by adding the methods required to
- * implement the HTTP/2 session functionalities.
- *
- * @param stream the stream to remove
- * @return whether the stream was removed
- */
- public boolean removeStream(IStream stream);
-
- /**
- *
Sends the given list of frames to create a new {@link Stream}.
- *
- * @param frames the list of frames to send
- * @param promise the promise that gets notified of the stream creation
- * @param listener the listener that gets notified of stream events
- */
- public void newStream(IStream.FrameList frames, Promise promise, Stream.Listener listener);
-
- /**
- *
Enqueues the given frames to be written to the connection.
- * @param stream the stream the frames belong to
- * @param frames the frames to enqueue
- * @param callback the callback that gets notified when the frames have been sent
- */
- public void frames(IStream stream, List extends Frame> frames, Callback callback);
-
- /**
- *
Enqueues the given PUSH_PROMISE frame to be written to the connection.
- *
Differently from {@link #frames(IStream, List, Callback)}, this method
- * generates atomically the stream id for the pushed stream.
- *
- * @param stream the stream associated to the pushed stream
- * @param promise the promise that gets notified of the pushed stream creation
- * @param frame the PUSH_PROMISE frame to enqueue
- * @param listener the listener that gets notified of pushed stream events
- */
- public void push(IStream stream, Promise promise, PushPromiseFrame frame, Stream.Listener listener);
-
- /**
- *
Enqueues the given DATA frame to be written to the connection.
- *
- * @param stream the stream the data frame belongs to
- * @param callback the callback that gets notified when the frame has been sent
- * @param frame the DATA frame to send
- */
- public void data(IStream stream, Callback callback, DataFrame frame);
-
- /**
- *
Updates the session send window by the given {@code delta}.
- *
- * @param delta the delta value (positive or negative) to add to the session send window
- * @return the previous value of the session send window
- */
- public int updateSendWindow(int delta);
-
- /**
- *
Updates the session receive window by the given {@code delta}.
- *
- * @param delta the delta value (positive or negative) to add to the session receive window
- * @return the previous value of the session receive window
- */
- public int updateRecvWindow(int delta);
-
- /**
- *
Callback method invoked when a WINDOW_UPDATE frame has been received.
- *
- * @param stream the stream the window update belongs to, or null if the window update belongs to the session
- * @param frame the WINDOW_UPDATE frame received
- */
- public void onWindowUpdate(IStream stream, WindowUpdateFrame frame);
-
- /**
- * @return whether the push functionality is enabled
- */
- public boolean isPushEnabled();
-
- /**
- *
- *
- * @return {@code true} if the session has expired
- * @see #onShutdown()
- * @see #close(int, String, Callback)
- */
- public boolean onIdleTimeout();
-
- /**
- *
Callback method invoked during an HTTP/1.1 to HTTP/2 upgrade requests
- * to process the given synthetic frame.
- *
- * @param frame the synthetic frame to process
- */
- public void onFrame(Frame frame);
-
- /**
- *
Callback method invoked when bytes are flushed to the network.
- *
- * @param bytes the number of bytes flushed to the network
- * @throws IOException if the flush should fail
- */
- public void onFlushed(long bytes) throws IOException;
-
- /**
- * @return the number of bytes written by this session
- */
- public long getBytesWritten();
-
- /**
- *
Callback method invoked when a DATA frame is received.
- *
- * @param frame the DATA frame received
- * @param callback the callback to notify when the frame has been processed
- */
- public void onData(DataFrame frame, Callback callback);
-
- /**
- *
Gracefully closes the session, returning a {@code CompletableFuture} that
- * is completed when all the streams currently being processed are completed.
- *
Implementation is idempotent, i.e. calling this method a second time
- * or concurrently results in a no-operation.
- *
- * @return a {@code CompletableFuture} that is completed when all the streams are completed
- */
- public CompletableFuture shutdown();
-}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java
deleted file mode 100644
index f28eb5f533e..00000000000
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java
+++ /dev/null
@@ -1,228 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2022 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.http2;
-
-import java.io.Closeable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import org.eclipse.jetty.http2.api.Stream;
-import org.eclipse.jetty.http2.frames.DataFrame;
-import org.eclipse.jetty.http2.frames.Frame;
-import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.http2.frames.StreamFrame;
-import org.eclipse.jetty.util.Attachable;
-import org.eclipse.jetty.util.Callback;
-
-/**
- *
The SPI interface for implementing an HTTP/2 stream.
- *
This class extends {@link Stream} by adding the methods required to
- * implement the HTTP/2 stream functionalities.
- */
-public interface IStream extends Stream, Attachable, Closeable
-{
- /**
- * @return whether this stream is local or remote
- */
- public boolean isLocal();
-
- @Override
- public ISession getSession();
-
- /**
- * @return the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream
- * @see #setListener(Stream.Listener)
- */
- public Listener getListener();
-
- // TODO: move this to Stream.
- public Data readData();
-
- /**
- * @param listener the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream
- * @see #getListener()
- */
- public void setListener(Listener listener);
-
- /**
- *
Sends the given list of frames.
- *
Typically used to send HTTP headers along with content and possibly trailers.
- *
- * @param frameList the list of frames to send
- * @param callback the callback that gets notified when the frames have been sent
- */
- void send(FrameList frameList, Callback callback);
-
- /**
- *
Processes the given {@code frame}, belonging to this stream.
- *
- * @param frame the frame to process
- * @param callback the callback to complete when frame has been processed
- */
- public void process(Frame frame, Callback callback);
-
- /**
- *
Updates the close state of this stream.
- *
- * @param update whether to update the close state
- * @param event the event that caused the close state update
- * @return whether the stream has been fully closed by this invocation
- */
- public boolean updateClose(boolean update, CloseState.Event event);
-
- /**
- *
Updates the stream send window by the given {@code delta}.
- *
- * @param delta the delta value (positive or negative) to add to the stream send window
- * @return the previous value of the stream send window
- */
- public int updateSendWindow(int delta);
-
- /**
- *
Updates the stream receive window by the given {@code delta}.
- *
- * @param delta the delta value (positive or negative) to add to the stream receive window
- * @return the previous value of the stream receive window
- */
- public int updateRecvWindow(int delta);
-
- /**
- *
Marks this stream as not idle so that the
- * {@link #getIdleTimeout() idle timeout} is postponed.
- */
- public void notIdle();
-
- /**
- * @return whether the stream is closed remotely.
- * @see #isClosed()
- */
- boolean isRemotelyClosed();
-
- /**
- * Fail all data queued in the stream and reset
- * demand to 0.
- * @param x the exception to fail the data with.
- * @return true if the end of the stream was reached, false otherwise.
- */
- boolean failAllData(Throwable x);
-
- /**
- * @return whether this stream has been reset (locally or remotely) or has been failed
- * @see #isReset()
- * @see Listener#onFailure(Stream, int, String, Throwable, Callback)
- */
- boolean isResetOrFailed();
-
- /**
- * Marks this stream as committed.
- *
- * @see #isCommitted()
- */
- void commit();
-
- /**
- * @return whether bytes for this stream have been sent to the remote peer.
- * @see #commit()
- */
- boolean isCommitted();
-
- /**
- *
An ordered list of frames belonging to the same stream.
- */
- public static class FrameList
- {
- private final List frames;
-
- /**
- *
Creates a frame list of just the given HEADERS frame.
- *
- * @param headers the HEADERS frame for the headers
- * @param data the DATA frame for the content, or null if there is no content
- * @param trailers the HEADERS frame for the trailers, or null if there are no trailers
- */
- public FrameList(HeadersFrame headers, DataFrame data, HeadersFrame trailers)
- {
- Objects.requireNonNull(headers);
- ArrayList frames = new ArrayList<>(3);
- int streamId = headers.getStreamId();
- if (data != null && data.getStreamId() != streamId)
- throw new IllegalArgumentException("Invalid stream ID for DATA frame " + data);
- if (trailers != null && trailers.getStreamId() != streamId)
- throw new IllegalArgumentException("Invalid stream ID for HEADERS frame " + trailers);
- frames.add(headers);
- if (data != null)
- frames.add(data);
- if (trailers != null)
- frames.add(trailers);
- this.frames = Collections.unmodifiableList(frames);
- }
-
- /**
- * @return the stream ID of the frames in this list
- */
- public int getStreamId()
- {
- return frames.get(0).getStreamId();
- }
-
- /**
- * @return a List of non-null frames
- */
- public List getFrames()
- {
- return frames;
- }
- }
-
- public static final class Data
- {
- private final DataFrame frame;
- private final Runnable complete;
-
- public Data(DataFrame frame, Runnable complete)
- {
- this.frame = frame;
- this.complete = complete;
- }
-
- public DataFrame frame()
- {
- return frame;
- }
-
- public void complete()
- {
- complete.run();
- }
- }
-}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java
index 576a14cc1ae..3d2b3cadc62 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/SimpleFlowControlStrategy.java
@@ -16,9 +16,9 @@ package org.eclipse.jetty.http2;
import java.util.ArrayList;
import java.util.List;
-import org.eclipse.jetty.http2.frames.Frame;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
-import org.eclipse.jetty.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,7 +37,7 @@ public class SimpleFlowControlStrategy extends AbstractFlowControlStrategy
}
@Override
- public void onDataConsumed(ISession session, IStream stream, int length)
+ public void onDataConsumed(Session session, Stream stream, int length)
{
if (length <= 0)
return;
@@ -46,10 +46,10 @@ public class SimpleFlowControlStrategy extends AbstractFlowControlStrategy
// This method is called when a whole flow controlled frame has been consumed.
// We send a WindowUpdate every time, even if the frame was very small.
- List frames = new ArrayList<>(2);
+ List frames = new ArrayList<>(2);
WindowUpdateFrame sessionFrame = new WindowUpdateFrame(0, length);
frames.add(sessionFrame);
- session.updateRecvWindow(length);
+ updateRecvWindow(session, length);
if (LOG.isDebugEnabled())
LOG.debug("Data consumed, increased session recv window by {} for {}", length, session);
@@ -64,12 +64,12 @@ public class SimpleFlowControlStrategy extends AbstractFlowControlStrategy
{
WindowUpdateFrame streamFrame = new WindowUpdateFrame(stream.getId(), length);
frames.add(streamFrame);
- stream.updateRecvWindow(length);
+ updateRecvWindow(stream, length);
if (LOG.isDebugEnabled())
LOG.debug("Data consumed, increased stream recv window by {} for {}", length, stream);
}
}
- session.frames(stream, frames, Callback.NOOP);
+ sendWindowUpdate(session, stream, frames);
}
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
index d0ad3c218a2..ce3efde79df 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
@@ -19,7 +19,6 @@ import java.util.Collection;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.GoAwayFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PingFrame;
@@ -36,7 +35,7 @@ import org.eclipse.jetty.util.Promise;
* Session session = ...;
* HeadersFrame frame = ...;
* Promise<Stream> promise = ...
- * session.newStream(frame, promise, new Stream.Listener.Adapter()
+ * session.newStream(frame, promise, new Stream.Listener()
* {
* public void onHeaders(Stream stream, HeadersFrame frame)
* {
@@ -122,6 +121,11 @@ public interface Session
*/
public boolean isClosed();
+ /**
+ * @return whether the push functionality is enabled
+ */
+ public boolean isPushEnabled();
+
/**
* @return a snapshot of all the streams currently belonging to this session
*/
@@ -169,6 +173,16 @@ public interface Session
return getRemoteAddress();
}
+ /**
+ *
Gracefully closes the session, returning a {@code CompletableFuture} that
+ * is completed when all the streams currently being processed are completed.
+ *
Implementation is idempotent, i.e. calling this method a second time
+ * or concurrently results in a no-operation.
+ *
+ * @return a {@code CompletableFuture} that is completed when all the streams are completed
+ */
+ public CompletableFuture shutdown();
+
/**
*
A {@link Listener} is the passive counterpart of a {@link Session} and
* receives events happening on an HTTP/2 connection.
@@ -191,7 +205,10 @@ public interface Session
* @return a (possibly empty or null) map containing SETTINGS configuration
* options to send.
*/
- public Map onPreface(Session session);
+ public default Map onPreface(Session session)
+ {
+ return null;
+ }
/**
*
Callback method invoked when a new stream is being created upon
@@ -201,15 +218,19 @@ public interface Session
* {@link Stream#headers(HeadersFrame, Callback)}.
*
Applications can detect whether request DATA frames will be arriving
* by testing {@link HeadersFrame#isEndStream()}. If the application is
- * interested in processing the DATA frames, it must return a
+ * interested in processing the DATA frames, it must demand for DATA
+ * frames using {@link Stream#demand()} and return a
* {@link Stream.Listener} implementation that overrides
- * {@link Stream.Listener#onData(Stream, DataFrame, Callback)}.
+ * {@link Stream.Listener#onDataAvailable(Stream)}.
*
* @param stream the newly created stream
* @param frame the HEADERS frame received
* @return a {@link Stream.Listener} that will be notified of stream events
*/
- public Stream.Listener onNewStream(Stream stream, HeadersFrame frame);
+ public default Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+ {
+ return null;
+ }
/**
*
Callback method invoked when a SETTINGS frame has been received.
@@ -217,7 +238,9 @@ public interface Session
* @param session the session
* @param frame the SETTINGS frame received
*/
- public void onSettings(Session session, SettingsFrame frame);
+ public default void onSettings(Session session, SettingsFrame frame)
+ {
+ }
/**
*
Callback method invoked when a PING frame has been received.
@@ -225,16 +248,20 @@ public interface Session
* @param session the session
* @param frame the PING frame received
*/
- public void onPing(Session session, PingFrame frame);
+ public default void onPing(Session session, PingFrame frame)
+ {
+ }
/**
*
Callback method invoked when a RST_STREAM frame has been received for an unknown stream.
*
* @param session the session
* @param frame the RST_STREAM frame received
- * @see Stream.Listener#onReset(Stream, ResetFrame)
+ * @see Stream.Listener#onReset(Stream, ResetFrame, Callback)
*/
- public void onReset(Session session, ResetFrame frame);
+ public default void onReset(Session session, ResetFrame frame)
+ {
+ }
/**
*
Callback method invoked when a GOAWAY frame has been received.
- */
- public static class Adapter implements Session.Listener
- {
- @Override
- public Map onPreface(Session session)
- {
- return null;
- }
-
- @Override
- public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
- {
- return null;
- }
-
- @Override
- public void onSettings(Session session, SettingsFrame frame)
- {
- }
-
- @Override
- public void onPing(Session session, PingFrame frame)
- {
- }
-
- @Override
- public void onReset(Session session, ResetFrame frame)
- {
- }
-
- @Override
- public void onClose(Session session, GoAwayFrame frame)
- {
- }
-
- @Override
- public boolean onIdleTimeout(Session session)
- {
- return true;
- }
-
- @Override
- public void onFailure(Session session, Throwable failure)
- {
- }
+ callback.succeeded();
}
}
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
index 23638134935..b9c21abae86 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
@@ -19,6 +19,8 @@ import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.io.Retainable;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
@@ -42,6 +44,11 @@ public interface Stream
*/
public int getId();
+ /**
+ * @return the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream
+ */
+ public Listener getListener();
+
/**
* @return the session this stream is associated to
*/
@@ -92,6 +99,34 @@ public interface Stream
*/
public void push(PushPromiseFrame frame, Promise promise, Listener listener);
+ /**
+ *
Reads DATA frames from this stream, wrapping them in retainable {@link Data}
+ * objects.
+ *
The returned {@link Stream.Data} object may be {@code null}, indicating
+ * that the end of the read side of the stream has not yet been reached, which
+ * may happen in these cases:
+ *
+ *
not all the bytes have been received so far, for example the remote
+ * peer did not send them yet, or they are in-flight
+ *
all the bytes have been received, but there is a trailer HEADERS
+ * frame to be received to indicate the end of the read side of the
+ * stream
+ *
+ *
When the returned {@link Stream.Data} object is not {@code null},
+ * applications must call, either immediately or later (possibly
+ * asynchronously) {@link Stream.Data#release()} to notify the
+ * implementation that the bytes have been processed.
+ *
{@link Stream.Data} objects may be stored away for later, asynchronous,
+ * processing (for example, to process them only when all of them have been
+ * received).
+ *
+ * @return a {@link Stream.Data} object containing the DATA frame,
+ * or null if no DATA frame is available
+ * @see #demand()
+ * @see Listener#onDataAvailable(Stream)
+ */
+ public Data readData();
+
/**
*
Sends the given DATA {@code frame}.
*
@@ -144,11 +179,22 @@ public interface Stream
*/
public Object removeAttribute(String key);
+ /**
+ * @return whether this stream is local or remote
+ */
+ public boolean isLocal();
+
/**
* @return whether this stream has been reset
*/
public boolean isReset();
+ /**
+ * @return whether the stream is closed remotely.
+ * @see #isClosed()
+ */
+ boolean isRemotelyClosed();
+
/**
* @return whether this stream is closed, both locally and remotely.
*/
@@ -168,12 +214,26 @@ public interface Stream
public void setIdleTimeout(long idleTimeout);
/**
- *
Demands {@code n} more {@code DATA} frames for this stream.
+ *
Demands more {@code DATA} frames for this stream, causing
+ * {@link Listener#onDataAvailable(Stream)} to be invoked, possibly at a later time,
+ * when the stream has data to be read.
+ *
This method is idempotent: calling it when there already is an
+ * outstanding demand to invoke {@link Listener#onDataAvailable(Stream)}
+ * is a no-operation.
+ *
The thread invoking this method may invoke directly
+ * {@link Listener#onDataAvailable(Stream)}, unless another thread
+ * that must invoke {@link Listener#onDataAvailable(Stream)}
+ * notices the outstanding demand first.
+ *
When all bytes have been read (via {@link #readData()}), further
+ * invocations of this method are a no-operation.
+ *
It is always guaranteed that invoking this method from within
+ * {@code onDataAvailable(Stream)} will not cause a
+ * {@link StackOverflowError}.
*
- * @param n the increment of the demand, must be greater than zero
- * @see Listener#onDataDemanded(Stream, DataFrame, Callback)
+ * @see #readData()
+ * @see Listener#onDataAvailable(Stream)
*/
- public void demand(long n);
+ public void demand();
/**
*
A {@link Stream.Listener} is the passive counterpart of a {@link Stream} and receives
@@ -181,7 +241,7 @@ public interface Stream
*
HTTP/2 data is flow controlled - this means that only a finite number of data events
* are delivered, until the flow control window is exhausted.
*
Applications control the delivery of data events by requesting them via
- * {@link Stream#demand(long)}; the first event is always delivered, while subsequent
+ * {@link Stream#demand()}; the first event is always delivered, while subsequent
* events must be explicitly demanded.
*
Applications control the HTTP/2 flow control by completing the callback associated
* with data events - this allows the implementation to recycle the data buffer and
@@ -207,7 +267,10 @@ public interface Stream
* @param stream the stream
* @param frame the HEADERS frame received
*/
- public void onHeaders(Stream stream, HeadersFrame frame);
+ public default void onHeaders(Stream stream, HeadersFrame frame)
+ {
+ stream.demand();
+ }
/**
*
Callback method invoked when a PUSH_PROMISE frame has been received.
@@ -216,45 +279,66 @@ public interface Stream
* @param frame the PUSH_PROMISE frame received
* @return a Stream.Listener that will be notified of pushed stream events
*/
- public Listener onPush(Stream stream, PushPromiseFrame frame);
-
- /**
- *
Callback method invoked before notifying the first DATA frame.
- *
The default implementation initializes the demand for DATA frames.
Callback method invoked when a DATA frame has been received.
+ *
Callback method invoked if the application has expressed
+ * {@link Stream#demand() demand} for DATA frames, and if there
+ * may be content available.
+ *
Applications that wish to handle DATA frames should call
+ * {@link Stream#demand()} for this method to be invoked when
+ * the data is available.
+ *
Server applications should typically demand from {@link #onNewStream(Stream)}
+ * (upon receiving an HTTP request), while client applications
+ * should typically demand from {@link #onHeaders(Stream, HeadersFrame)}
+ * (upon receiving an HTTP response).
+ *
Just prior calling this method, the outstanding demand is
+ * cancelled; applications that implement this method should read
+ * content calling {@link Stream#readData()}, and call
+ * {@link Stream#demand()} to signal to the implementation to call
+ * again this method when there may be more content available.
+ *
Only one thread at a time invokes this method, although it
+ * may not be the same thread across different invocations.
+ *
It is always guaranteed that invoking {@link Stream#demand()}
+ * from within this method will not cause a {@link StackOverflowError}.
+ *
Typical usage:
+ *
+ * class MyStreamListener implements Stream.Listener
+ * {
+ * @Override
+ * public void onDataAvailable(Stream stream)
+ * {
+ * // Read a chunk of the content.
+ * Stream.Data data = stream.readData();
+ * if (data == null)
+ * {
+ * // No data available now, demand to be called back.
+ * stream.demand();
+ * }
+ * else
+ * {
+ * // Process the content.
+ * process(data.getByteBuffer());
+ * // Notify that the content has been consumed.
+ * data.release();
+ * if (!data.frame().isEndStream())
+ * {
+ * // Demand to be called back.
+ * stream.demand();
+ * }
+ * }
+ * }
+ * }
+ *
*
* @param stream the stream
- * @param frame the DATA frame received
- * @param callback the callback to complete when the bytes of the DATA frame have been consumed
- * @see #onDataDemanded(Stream, DataFrame, Callback)
+ * @see Stream#demand()
*/
- public default void onData(Stream stream, DataFrame frame, Callback callback)
+ public default void onDataAvailable(Stream stream)
{
- callback.succeeded();
- }
-
- /**
- *
Callback method invoked when a DATA frame has been demanded.
- *
Implementations of this method must arrange to call (within the
- * method or otherwise asynchronously) {@link #demand(long)}.
- *
- * @param stream the stream
- * @param frame the DATA frame received
- * @param callback the callback to complete when the bytes of the DATA frame have been consumed
- */
- public default void onDataDemanded(Stream stream, DataFrame frame, Callback callback)
- {
- onData(stream, frame, callback);
- stream.demand(1);
}
/**
@@ -266,26 +350,7 @@ public interface Stream
*/
public default void onReset(Stream stream, ResetFrame frame, Callback callback)
{
- try
- {
- onReset(stream, frame);
- callback.succeeded();
- }
- catch (Throwable x)
- {
- callback.failed(x);
- }
- }
-
- /**
- *
Callback method invoked when a RST_STREAM frame has been received for this stream.
- *
- * @param stream the stream
- * @param frame the RST_FRAME received
- * @see Session.Listener#onReset(Session, ResetFrame)
- */
- public default void onReset(Stream stream, ResetFrame frame)
- {
+ callback.succeeded();
}
/**
@@ -296,7 +361,10 @@ public interface Stream
* @return true to reset the stream, false to ignore the idle timeout
* @see #getIdleTimeout()
*/
- public boolean onIdleTimeout(Stream stream, Throwable x);
+ public default boolean onIdleTimeout(Stream stream, Throwable x)
+ {
+ return true;
+ }
/**
*
Callback method invoked when the stream failed.
@@ -320,38 +388,53 @@ public interface Stream
public default void onClosed(Stream stream)
{
}
+ }
- /**
- *
Empty implementation of {@link Listener}
- */
- public static class Adapter implements Listener
+ /**
+ *
A {@link Retainable} wrapper of a {@link DataFrame}.
+ */
+ public abstract static class Data implements Retainable
+ {
+ public static Data eof(int streamId)
{
- @Override
- public void onHeaders(Stream stream, HeadersFrame frame)
- {
- }
+ return new Data.EOF(streamId);
+ }
- @Override
- public Listener onPush(Stream stream, PushPromiseFrame frame)
- {
- return null;
- }
+ private final DataFrame frame;
- @Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
- {
- callback.succeeded();
- }
+ public Data(DataFrame frame)
+ {
+ this.frame = frame;
+ }
- @Override
- public void onReset(Stream stream, ResetFrame frame)
- {
- }
+ public DataFrame frame()
+ {
+ return frame;
+ }
- @Override
- public boolean onIdleTimeout(Stream stream, Throwable x)
+ @Override
+ public void retain()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean release()
+ {
+ return true;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "%s@%x[%s]".formatted(getClass().getSimpleName(), hashCode(), frame());
+ }
+
+ private static class EOF extends Data
+ {
+ public EOF(int streamId)
{
- return true;
+ super(new DataFrame(streamId, BufferUtil.EMPTY_BUFFER, true));
}
}
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java
index 6a6b2976d22..7600dbcdbfc 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/server/ServerSessionListener.java
@@ -25,16 +25,7 @@ public interface ServerSessionListener extends Session.Listener
*
* @param session the session
*/
- public void onAccept(Session session);
-
- /**
- *
Empty implementation of {@link ServerSessionListener}
- */
- public static class Adapter extends Session.Listener.Adapter implements ServerSessionListener
+ public default void onAccept(Session session)
{
- @Override
- public void onAccept(Session session)
- {
- }
}
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Channel.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Channel.java
index 4abce8dafdb..f3dcae40b06 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Channel.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Channel.java
@@ -15,7 +15,6 @@ package org.eclipse.jetty.http2.internal;
import java.util.function.Consumer;
-import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
@@ -32,7 +31,7 @@ public interface HTTP2Channel
*/
public interface Client
{
- public void onData(DataFrame frame, Callback callback);
+ public void onDataAvailable();
public boolean onTimeout(Throwable failure);
@@ -47,7 +46,7 @@ public interface HTTP2Channel
*/
public interface Server
{
- public Runnable onData(DataFrame frame, Callback callback);
+ public Runnable onDataAvailable();
public Runnable onTrailer(HeadersFrame frame);
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java
index 9c46af3f63f..87e7d09fe9a 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Connection.java
@@ -20,12 +20,13 @@ import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
-import org.eclipse.jetty.http2.ISession;
+import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.internal.parser.Parser;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.io.RetainableByteBuffer;
import org.eclipse.jetty.io.RetainableByteBufferPool;
import org.eclipse.jetty.io.WriteFlusher;
@@ -48,13 +49,13 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
private final AtomicLong bytesIn = new AtomicLong();
private final RetainableByteBufferPool retainableByteBufferPool;
private final Parser parser;
- private final ISession session;
+ private final HTTP2Session session;
private final int bufferSize;
private final ExecutionStrategy strategy;
private boolean useInputDirectByteBuffers;
private boolean useOutputDirectByteBuffers;
- protected HTTP2Connection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize)
+ protected HTTP2Connection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, HTTP2Session session, int bufferSize)
{
super(endPoint, executor);
this.retainableByteBufferPool = retainableByteBufferPool;
@@ -69,14 +70,14 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
@Override
public long getMessagesIn()
{
- HTTP2Session session = (HTTP2Session)getSession();
+ HTTP2Session session = getSession();
return session.getStreamsOpened();
}
@Override
public long getMessagesOut()
{
- HTTP2Session session = (HTTP2Session)getSession();
+ HTTP2Session session = getSession();
return session.getStreamsClosed();
}
@@ -92,7 +93,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
return session.getBytesWritten();
}
- public ISession getSession()
+ public HTTP2Session getSession()
{
return session;
}
@@ -196,7 +197,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
private void offerTask(Runnable task)
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
tasks.offer(task);
}
@@ -226,7 +227,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
private Runnable pollTask()
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
return tasks.poll();
}
@@ -257,7 +258,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
{
Runnable task = pollTask();
if (LOG.isDebugEnabled())
- LOG.debug("Dequeued task {}", String.valueOf(task));
+ LOG.debug("Dequeued task {}", task);
if (task != null)
return task;
@@ -404,8 +405,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
{
NetworkBuffer networkBuffer = producer.networkBuffer;
networkBuffer.retain();
- Callback callback = networkBuffer;
- session.onData(frame, callback);
+ session.onData(new StreamData(frame, networkBuffer));
}
@Override
@@ -416,7 +416,30 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
}
}
- private class NetworkBuffer implements Callback
+ private static class StreamData extends Stream.Data
+ {
+ private final Retainable retainable;
+
+ private StreamData(DataFrame frame, Retainable retainable)
+ {
+ super(frame);
+ this.retainable = retainable;
+ }
+
+ @Override
+ public void retain()
+ {
+ retainable.retain();
+ }
+
+ @Override
+ public boolean release()
+ {
+ return retainable.release();
+ }
+ }
+
+ private class NetworkBuffer implements Retainable
{
private final RetainableByteBuffer delegate;
@@ -440,46 +463,27 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
return delegate.hasRemaining();
}
- public boolean release()
- {
- return delegate.release();
- }
-
+ @Override
public void retain()
{
delegate.retain();
}
+ @Override
+ public boolean release()
+ {
+ if (delegate.release())
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Released retained {}", this);
+ return true;
+ }
+ return false;
+ }
+
private void put(ByteBuffer source)
{
BufferUtil.append(delegate.getBuffer(), source);
}
-
- @Override
- public void succeeded()
- {
- completed(null);
- }
-
- @Override
- public void failed(Throwable failure)
- {
- completed(failure);
- }
-
- private void completed(Throwable failure)
- {
- if (delegate.release())
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Released retained {}", this, failure);
- }
- }
-
- @Override
- public InvocationType getInvocationType()
- {
- return InvocationType.NON_BLOCKING;
- }
}
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java
index 66212b9f181..dc960759ca4 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Flusher.java
@@ -26,7 +26,6 @@ import java.util.Queue;
import java.util.Set;
import org.eclipse.jetty.http2.FlowControlStrategy;
-import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.FrameType;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
@@ -69,10 +68,10 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
return invocationType;
}
- public void window(IStream stream, WindowUpdateFrame frame)
+ public void window(HTTP2Stream stream, WindowUpdateFrame frame)
{
Throwable closed;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
closed = terminated;
if (closed == null)
@@ -86,7 +85,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
public boolean prepend(Entry entry)
{
Throwable closed;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
closed = terminated;
if (closed == null)
@@ -105,7 +104,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
public boolean append(Entry entry)
{
Throwable closed;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
closed = terminated;
if (closed == null)
@@ -124,7 +123,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
public boolean append(List list)
{
Throwable closed;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
closed = terminated;
if (closed == null)
@@ -142,7 +141,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
private int getWindowQueueSize()
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
return windows.size();
}
@@ -150,7 +149,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
public int getFrameQueueSize()
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
return entries.size();
}
@@ -162,7 +161,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
if (LOG.isDebugEnabled())
LOG.debug("Flushing {}", session);
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
if (terminated != null)
throw terminated;
@@ -354,7 +353,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
Throwable closed;
Set allEntries;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
closed = terminated;
terminated = x;
@@ -383,7 +382,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
void terminate(Throwable cause)
{
Throwable closed;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
closed = terminated;
terminated = cause;
@@ -425,9 +424,9 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
public abstract static class Entry extends Callback.Nested
{
protected final Frame frame;
- protected final IStream stream;
+ protected final HTTP2Stream stream;
- protected Entry(Frame frame, IStream stream, Callback callback)
+ protected Entry(Frame frame, HTTP2Stream stream, Callback callback)
{
super(callback);
this.frame = frame;
@@ -514,10 +513,10 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
private class WindowEntry
{
- private final IStream stream;
+ private final HTTP2Stream stream;
private final WindowUpdateFrame frame;
- public WindowEntry(IStream stream, WindowUpdateFrame frame)
+ public WindowEntry(HTTP2Stream stream, WindowUpdateFrame frame)
{
this.stream = stream;
this.frame = frame;
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java
index 2d5d464d25b..8a23f69c6d0 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Session.java
@@ -40,8 +40,6 @@ import java.util.stream.Collectors;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.CloseState;
import org.eclipse.jetty.http2.FlowControlStrategy;
-import org.eclipse.jetty.http2.ISession;
-import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
@@ -82,11 +80,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ManagedObject
-public abstract class HTTP2Session extends ContainerLifeCycle implements ISession, Parser.Listener
+public abstract class HTTP2Session extends ContainerLifeCycle implements Session, Parser.Listener
{
private static final Logger LOG = LoggerFactory.getLogger(HTTP2Session.class);
- private final ConcurrentMap streams = new ConcurrentHashMap<>();
+ private final ConcurrentMap streams = new ConcurrentHashMap<>();
private final AtomicLong streamsOpened = new AtomicLong();
private final AtomicLong streamsClosed = new AtomicLong();
private final StreamsState streamsState = new StreamsState();
@@ -221,7 +219,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return generator;
}
- @Override
public long getBytesWritten()
{
return bytesWritten.get();
@@ -230,17 +227,18 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
@Override
public void onData(DataFrame frame)
{
- onData(frame, Callback.NOOP);
+ // This method should never be called, the one below should.
+ throw new UnsupportedOperationException();
}
- @Override
- public void onData(DataFrame frame, Callback callback)
+ public void onData(Stream.Data data)
{
if (LOG.isDebugEnabled())
- LOG.debug("Received {} on {}", frame, this);
+ LOG.debug("Received {} on {}", data, this);
+ DataFrame frame = data.frame();
int streamId = frame.getStreamId();
- IStream stream = getStream(streamId);
+ HTTP2Stream stream = getStream(streamId);
// SPEC: the session window must be updated even if the stream is null.
// The flow control length includes the padding bytes.
@@ -251,7 +249,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
if (getRecvWindow() < 0)
{
- onSessionFailure(ErrorCode.FLOW_CONTROL_ERROR.code, "session_window_exceeded", callback);
+ onSessionFailure(ErrorCode.FLOW_CONTROL_ERROR.code, "session_window_exceeded", toCallback(data));
}
else
{
@@ -259,11 +257,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
// It's a bad client, it does not deserve to be
// treated gently by just resetting the stream.
- onSessionFailure(ErrorCode.FLOW_CONTROL_ERROR.code, "stream_window_exceeded", callback);
+ onSessionFailure(ErrorCode.FLOW_CONTROL_ERROR.code, "stream_window_exceeded", toCallback(data));
}
else
{
- stream.process(frame, new DataCallback(callback, stream, flowControlLength));
+ stream.process(new StreamData(data, stream, flowControlLength));
}
}
}
@@ -275,12 +273,36 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
// otherwise other requests will be stalled.
flowControl.onDataConsumed(this, null, flowControlLength);
if (isStreamClosed(streamId))
- reset(null, new ResetFrame(streamId, ErrorCode.STREAM_CLOSED_ERROR.code), callback);
+ reset(null, new ResetFrame(streamId, ErrorCode.STREAM_CLOSED_ERROR.code), toCallback(data));
else
- onSessionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_data_frame", callback);
+ onSessionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_data_frame", toCallback(data));
}
}
+ private Callback toCallback(Stream.Data data)
+ {
+ return new Callback()
+ {
+ @Override
+ public void succeeded()
+ {
+ data.release();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ data.release();
+ }
+
+ @Override
+ public InvocationType getInvocationType()
+ {
+ return InvocationType.NON_BLOCKING;
+ }
+ };
+ }
+
private boolean isStreamClosed(int streamId)
{
return isLocalStream(streamId) ? isLocalStreamClosed(streamId) : isRemoteStreamClosed(streamId);
@@ -318,7 +340,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
LOG.debug("Received {} on {}", frame, this);
int streamId = frame.getStreamId();
- IStream stream = getStream(streamId);
+ HTTP2Stream stream = getStream(streamId);
if (stream != null)
{
stream.process(frame, new OnResetCallback());
@@ -359,62 +381,54 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
int value = entry.getValue();
switch (key)
{
- case SettingsFrame.HEADER_TABLE_SIZE:
+ case SettingsFrame.HEADER_TABLE_SIZE ->
{
if (LOG.isDebugEnabled())
LOG.debug("Updating HPACK header table size to {} for {}", value, this);
generator.setHeaderTableSize(value);
- break;
}
- case SettingsFrame.ENABLE_PUSH:
+ case SettingsFrame.ENABLE_PUSH ->
{
boolean enabled = value == 1;
if (LOG.isDebugEnabled())
LOG.debug("{} push for {}", enabled ? "Enabling" : "Disabling", this);
pushEnabled = enabled;
- break;
}
- case SettingsFrame.MAX_CONCURRENT_STREAMS:
+ case SettingsFrame.MAX_CONCURRENT_STREAMS ->
{
if (LOG.isDebugEnabled())
LOG.debug("Updating max local concurrent streams to {} for {}", value, this);
maxLocalStreams = value;
- break;
}
- case SettingsFrame.INITIAL_WINDOW_SIZE:
+ case SettingsFrame.INITIAL_WINDOW_SIZE ->
{
if (LOG.isDebugEnabled())
LOG.debug("Updating initial stream window size to {} for {}", value, this);
flowControl.updateInitialStreamWindow(this, value, false);
- break;
}
- case SettingsFrame.MAX_FRAME_SIZE:
+ case SettingsFrame.MAX_FRAME_SIZE ->
{
if (LOG.isDebugEnabled())
LOG.debug("Updating max frame size to {} for {}", value, this);
generator.setMaxFrameSize(value);
- break;
}
- case SettingsFrame.MAX_HEADER_LIST_SIZE:
+ case SettingsFrame.MAX_HEADER_LIST_SIZE ->
{
if (LOG.isDebugEnabled())
LOG.debug("Updating max header list size to {} for {}", value, this);
generator.setMaxHeaderListSize(value);
- break;
}
- case SettingsFrame.ENABLE_CONNECT_PROTOCOL:
+ case SettingsFrame.ENABLE_CONNECT_PROTOCOL ->
{
boolean enabled = value == 1;
if (LOG.isDebugEnabled())
LOG.debug("{} CONNECT protocol for {}", enabled ? "Enabling" : "Disabling", this);
connectProtocolEnabled = enabled;
- break;
}
- default:
+ default ->
{
if (LOG.isDebugEnabled())
LOG.debug("Unknown setting {}:{} for {}", key, value, this);
- break;
}
}
}
@@ -470,7 +484,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
int windowDelta = frame.getWindowDelta();
if (streamId > 0)
{
- IStream stream = getStream(streamId);
+ HTTP2Stream stream = getStream(streamId);
if (stream != null)
{
int streamSendWindow = stream.updateSendWindow(0);
@@ -500,8 +514,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
- @Override
- public void onWindowUpdate(IStream stream, WindowUpdateFrame frame)
+ public void onWindowUpdate(HTTP2Stream stream, WindowUpdateFrame frame)
{
// WindowUpdateFrames arrive concurrently with writes.
// Increasing (or reducing) the window size concurrently
@@ -522,7 +535,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
Throwable failure = toFailure(error, reason);
if (LOG.isDebugEnabled())
LOG.debug("Stream #{} failure {}", streamId, this, failure);
- IStream stream = getStream(streamId);
+ HTTP2Stream stream = getStream(streamId);
if (stream != null)
failStream(stream, error, reason, failure, callback);
else
@@ -567,7 +580,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
notifyFailure(this, failure, countCallback);
}
- private void failStreams(Predicate matcher, String reason, boolean reset)
+ private void failStreams(Predicate matcher, String reason, boolean reset)
{
int error = ErrorCode.CANCEL_STREAM_ERROR.code;
Throwable failure = toFailure(error, reason);
@@ -575,7 +588,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
if (stream.isClosed())
continue;
- if (!matcher.test((IStream)stream))
+ if (!matcher.test(stream))
continue;
if (LOG.isDebugEnabled())
LOG.debug("Failing stream {} of {}", stream, this);
@@ -587,7 +600,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private void failStream(Stream stream, int error, String reason, Throwable failure, Callback callback)
{
- ((IStream)stream).process(new FailureFrame(error, reason, failure), callback);
+ ((HTTP2Stream)stream).process(new FailureFrame(error, reason, failure), callback);
}
private Throwable toFailure(int error, String reason)
@@ -598,11 +611,10 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
@Override
public void newStream(HeadersFrame frame, Promise promise, Stream.Listener listener)
{
- newStream(new IStream.FrameList(frame), promise, listener);
+ newStream(new HTTP2Stream.FrameList(frame), promise, listener);
}
- @Override
- public void newStream(IStream.FrameList frames, Promise promise, Stream.Listener listener)
+ public void newStream(HTTP2Stream.FrameList frames, Promise promise, Stream.Listener listener)
{
streamsState.newLocalStream(frames, promise, listener);
}
@@ -619,7 +631,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return streamsState.newUpgradeStream(frame, listener, failFn);
}
- protected IStream newStream(int streamId, MetaData.Request request, boolean local)
+ protected HTTP2Stream newStream(int streamId, MetaData.Request request, boolean local)
{
return new HTTP2Stream(this, streamId, request, local);
}
@@ -630,8 +642,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return streamsState.priority(frame, callback);
}
- @Override
- public void push(IStream stream, Promise promise, PushPromiseFrame frame, Stream.Listener listener)
+ public void push(Stream stream, Promise promise, PushPromiseFrame frame, Stream.Listener listener)
{
streamsState.push(frame, new Promise.Wrapper<>(promise)
{
@@ -640,7 +651,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
// Pushed streams are implicitly remotely closed.
// They are closed when sending an end-stream DATA frame.
- ((IStream)pushedStream).updateClose(true, CloseState.Event.RECEIVED);
+ ((HTTP2Stream)pushedStream).updateClose(true, CloseState.Event.RECEIVED);
super.succeeded(pushedStream);
}
}, listener);
@@ -661,7 +672,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
control(null, callback, frame);
}
- void reset(IStream stream, ResetFrame frame, Callback callback)
+ void reset(HTTP2Stream stream, ResetFrame frame, Callback callback)
{
control(stream, Callback.from(() ->
{
@@ -730,13 +741,12 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return streamsState.getCloseState();
}
- private void control(IStream stream, Callback callback, Frame frame)
+ private void control(HTTP2Stream stream, Callback callback, Frame frame)
{
frames(stream, List.of(frame), callback);
}
- @Override
- public void frames(IStream stream, List extends Frame> frames, Callback callback)
+ public void frames(HTTP2Stream stream, List extends Frame> frames, Callback callback)
{
// We want to generate as late as possible to allow re-prioritization;
// generation will happen while processing the entries.
@@ -754,15 +764,14 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
- private HTTP2Flusher.Entry newEntry(Frame frame, IStream stream, Callback callback)
+ private HTTP2Flusher.Entry newEntry(Frame frame, HTTP2Stream stream, Callback callback)
{
return frame.getType() == FrameType.DATA
? new DataEntry((DataFrame)frame, stream, callback)
: new ControlEntry(frame, stream, callback);
}
- @Override
- public void data(IStream stream, Callback callback, DataFrame frame)
+ public void data(HTTP2Stream stream, DataFrame frame, Callback callback)
{
// We want to generate as late as possible to allow re-prioritization.
frame(newEntry(frame, stream, callback), true);
@@ -782,7 +791,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
- protected IStream createLocalStream(int streamId, MetaData.Request request, Consumer failFn)
+ protected HTTP2Stream createLocalStream(int streamId, MetaData.Request request, Consumer failFn)
{
while (true)
{
@@ -800,7 +809,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
break;
}
- IStream stream = newStream(streamId, request, true);
+ HTTP2Stream stream = newStream(streamId, request, true);
if (streams.putIfAbsent(streamId, stream) == null)
{
stream.setIdleTimeout(getStreamIdleTimeout());
@@ -817,7 +826,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
- protected IStream createRemoteStream(int streamId, MetaData.Request request)
+ protected HTTP2Stream createRemoteStream(int streamId, MetaData.Request request)
{
// This stream has been seen the server.
// Even if the stream cannot be created because this peer is closing,
@@ -851,7 +860,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
break;
}
- IStream stream = newStream(streamId, request, false);
+ HTTP2Stream stream = newStream(streamId, request, false);
if (streams.putIfAbsent(streamId, stream) == null)
{
stream.setIdleTimeout(getStreamIdleTimeout());
@@ -878,11 +887,10 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
remoteStreamCount.add(deltaStreams, deltaClosing);
}
- @Override
- public boolean removeStream(IStream stream)
+ public boolean removeStream(Stream stream)
{
int streamId = stream.getId();
- IStream removed = streams.remove(streamId);
+ HTTP2Stream removed = streams.remove(streamId);
if (removed == null)
return false;
if (LOG.isDebugEnabled())
@@ -906,7 +914,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
@Override
- public IStream getStream(int streamId)
+ public HTTP2Stream getStream(int streamId)
{
return streams.get(streamId);
}
@@ -953,13 +961,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return recvWindow.get();
}
- @Override
public int updateSendWindow(int delta)
{
return sendWindow.getAndAdd(delta);
}
- @Override
public int updateRecvWindow(int delta)
{
return recvWindow.getAndAdd(delta);
@@ -990,7 +996,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
* @see #close(int, String, Callback)
* @see #onIdleTimeout()
*/
- @Override
public void onShutdown()
{
streamsState.onShutdown();
@@ -1004,7 +1009,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
* @see #close(int, String, Callback)
* @see #onShutdown()
*/
- @Override
public boolean onIdleTimeout()
{
return streamsState.onIdleTimeout();
@@ -1015,7 +1019,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
streamsState.idleTime = System.nanoTime();
}
- @Override
public void onFrame(Frame frame)
{
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "upgrade");
@@ -1033,14 +1036,14 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
streamsState.onStreamCreated();
}
- protected final void onStreamOpened(IStream stream)
+ protected final void onStreamOpened(Stream stream)
{
if (LOG.isDebugEnabled())
LOG.debug("Opened stream {} for {}", stream, this);
streamsOpened.incrementAndGet();
}
- private void onStreamClosed(IStream stream)
+ private void onStreamClosed(Stream stream)
{
if (LOG.isDebugEnabled())
LOG.debug("Closed stream {} for {}", stream, this);
@@ -1054,7 +1057,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
streamsState.onStreamDestroyed();
}
- @Override
public void onFlushed(long bytes) throws IOException
{
flusher.onFlushed(bytes);
@@ -1187,7 +1189,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
- protected void notifyHeaders(IStream stream, HeadersFrame frame)
+ protected void notifyHeaders(Stream stream, HeadersFrame frame)
{
Stream.Listener listener = stream.getListener();
if (listener == null)
@@ -1232,7 +1234,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
private int frameBytes;
- private ControlEntry(Frame frame, IStream stream, Callback callback)
+ private ControlEntry(Frame frame, HTTP2Stream stream, Callback callback)
{
super(frame, stream, callback);
}
@@ -1278,23 +1280,17 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
switch (frame.getType())
{
- case HEADERS:
+ case HEADERS ->
{
HeadersFrame headersFrame = (HeadersFrame)frame;
stream.updateClose(headersFrame.isEndStream(), CloseState.Event.BEFORE_SEND);
- break;
}
- case SETTINGS:
+ case SETTINGS ->
{
SettingsFrame settingsFrame = (SettingsFrame)frame;
Integer initialWindow = settingsFrame.getSettings().get(SettingsFrame.INITIAL_WINDOW_SIZE);
if (initialWindow != null)
flowControl.updateInitialStreamWindow(HTTP2Session.this, initialWindow, true);
- break;
- }
- default:
- {
- break;
}
}
}
@@ -1315,23 +1311,17 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
switch (frame.getType())
{
- case HEADERS:
+ case HEADERS ->
{
HeadersFrame headersFrame = (HeadersFrame)frame;
if (headersFrame.getMetaData().isRequest())
onStreamOpened(stream);
if (stream.updateClose(headersFrame.isEndStream(), CloseState.Event.AFTER_SEND))
removeStream(stream);
- break;
}
- case WINDOW_UPDATE:
+ case WINDOW_UPDATE ->
{
flowControl.windowUpdate(HTTP2Session.this, stream, (WindowUpdateFrame)frame);
- break;
- }
- default:
- {
- break;
}
}
@@ -1346,7 +1336,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private int dataBytes;
private int dataRemaining;
- private DataEntry(DataFrame frame, IStream stream, Callback callback)
+ private DataEntry(DataFrame frame, HTTP2Stream stream, Callback callback)
{
super(frame, stream, callback);
// We don't do any padding, so the flow control length is
@@ -1439,42 +1429,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
- private class DataCallback extends Callback.Nested
- {
- private final IStream stream;
- private final int flowControlLength;
-
- public DataCallback(Callback callback, IStream stream, int flowControlLength)
- {
- super(callback);
- this.stream = stream;
- this.flowControlLength = flowControlLength;
- }
-
- @Override
- public void succeeded()
- {
- complete();
- super.succeeded();
- }
-
- @Override
- public void failed(Throwable x)
- {
- // Consume also in case of failures, to free the
- // session flow control window for other streams.
- complete();
- super.failed(x);
- }
-
- private void complete()
- {
- notIdle();
- stream.notIdle();
- flowControl.onDataConsumed(HTTP2Session.this, stream, flowControlLength);
- }
- }
-
private class OnResetCallback implements Callback
{
@Override
@@ -1531,7 +1485,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private CloseState getCloseState()
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
return closed;
}
@@ -1540,7 +1494,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private CompletableFuture shutdown()
{
CompletableFuture future;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
if (shutdownCallback != null)
return shutdownCallback;
@@ -1554,11 +1508,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
boolean sendGoAway = false;
boolean tryRunZeroStreamsAction = false;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
+ case NOT_CLOSED ->
{
goAwaySent = frame;
closed = CloseState.LOCALLY_CLOSED;
@@ -1574,9 +1528,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
};
tryRunZeroStreamsAction = streamCount.get() == 0;
}
- break;
}
- case LOCALLY_CLOSED:
+ case LOCALLY_CLOSED ->
{
if (frame.isGraceful())
{
@@ -1601,9 +1554,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
LOG.debug("Already sent, ignored GOAWAY {} for {}", frame, HTTP2Session.this);
}
}
- break;
}
- case REMOTELY_CLOSED:
+ case REMOTELY_CLOSED ->
{
goAwaySent = frame;
sendGoAway = true;
@@ -1632,14 +1584,12 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
tryRunZeroStreamsAction = streamCount.get() == 0;
}
}
- break;
}
- default:
+ default ->
{
// Already closing or closed, ignore it.
if (LOG.isDebugEnabled())
LOG.debug("Already closed, ignored {} for {}", frame, HTTP2Session.this);
- break;
}
}
}
@@ -1665,14 +1615,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
LOG.debug("Halting ({}) for {}", reason, HTTP2Session.this);
GoAwayFrame goAwayFrame = null;
GoAwayFrame goAwayFrameEvent;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
- case REMOTELY_CLOSED:
- case LOCALLY_CLOSED:
- case CLOSING:
+ case NOT_CLOSED, REMOTELY_CLOSED, LOCALLY_CLOSED, CLOSING ->
{
if (goAwaySent == null || goAwaySent.isGraceful())
goAwaySent = goAwayFrame = newGoAwayFrame(ErrorCode.NO_ERROR.code, reason);
@@ -1681,9 +1628,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
zeroStreamsAction = null;
if (failure != null)
failure = toFailure(ErrorCode.NO_ERROR.code, reason);
- break;
}
- default:
+ default ->
{
return;
}
@@ -1700,11 +1646,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
boolean failStreams = false;
boolean tryRunZeroStreamsAction = false;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
+ case NOT_CLOSED ->
{
goAwayRecv = frame;
if (frame.isGraceful())
@@ -1722,9 +1668,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
tryRunZeroStreamsAction = streamCount.get() == 0;
failStreams = true;
}
- break;
}
- case LOCALLY_CLOSED:
+ case LOCALLY_CLOSED ->
{
goAwayRecv = frame;
if (frame.isGraceful())
@@ -1750,9 +1695,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
failStreams = true;
}
}
- break;
}
- case REMOTELY_CLOSED:
+ case REMOTELY_CLOSED ->
{
if (frame.isGraceful())
{
@@ -1777,14 +1721,12 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
tryRunZeroStreamsAction = streamCount.get() == 0;
failStreams = true;
}
- break;
}
- default:
+ default ->
{
// Already closing or closed, ignore it.
if (LOG.isDebugEnabled())
LOG.debug("Already closed, ignored {} for {}", frame, HTTP2Session.this);
- break;
}
}
}
@@ -1797,7 +1739,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
// For example, a client that sent request with streamId=137 may send a GOAWAY(4),
// where streamId=4 is the last stream pushed by the server to the client.
// The server must not compare its local streamId=4 with remote streamId=137.
- Predicate failIf = stream -> stream.isLocal() && stream.getId() > frame.getLastStreamId();
+ Predicate failIf = stream -> stream.isLocal() && stream.getId() > frame.getLastStreamId();
failStreams(failIf, "closing", false);
}
@@ -1810,36 +1752,32 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
String reason = "input_shutdown";
Throwable cause = null;
boolean failStreams = false;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
- case LOCALLY_CLOSED:
+ case NOT_CLOSED, LOCALLY_CLOSED ->
{
if (LOG.isDebugEnabled())
LOG.debug("Unexpected ISHUT for {}", HTTP2Session.this);
closed = CloseState.CLOSING;
failure = cause = new ClosedChannelException();
- break;
}
- case REMOTELY_CLOSED:
+ case REMOTELY_CLOSED ->
{
closed = CloseState.CLOSING;
GoAwayFrame goAwayFrame = newGoAwayFrame(0, ErrorCode.NO_ERROR.code, reason);
zeroStreamsAction = () -> terminate(goAwayFrame);
failure = new ClosedChannelException();
failStreams = true;
- break;
}
- case CLOSING:
+ case CLOSING ->
{
if (failure == null)
failure = new ClosedChannelException();
failStreams = true;
- break;
}
- default:
+ default ->
{
if (LOG.isDebugEnabled())
LOG.debug("Already closed, ignoring ISHUT for {}", HTTP2Session.this);
@@ -1852,7 +1790,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
// Since nothing else will arrive from the other peer, reset
// the streams for which the other peer did not send all frames.
- Predicate failIf = stream -> !stream.isRemotelyClosed();
+ Predicate failIf = stream -> !stream.isRemotelyClosed();
failStreams(failIf, reason, false);
tryRunZeroStreamsAction();
}
@@ -1870,20 +1808,20 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
boolean sendGoAway = false;
GoAwayFrame goAwayFrame = null;
Throwable cause = null;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
+ case NOT_CLOSED ->
{
long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTime);
if (elapsed < endPoint.getIdleTimeout())
return false;
notify = true;
- break;
}
+
// Timed out while waiting for closing events, fail all the streams.
- case LOCALLY_CLOSED:
+ case LOCALLY_CLOSED ->
{
if (goAwaySent.isGraceful())
{
@@ -1894,9 +1832,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
closed = CloseState.CLOSING;
zeroStreamsAction = null;
failure = cause = new TimeoutException("Session idle timeout expired");
- break;
}
- case REMOTELY_CLOSED:
+ case REMOTELY_CLOSED ->
{
goAwaySent = newGoAwayFrame(ErrorCode.NO_ERROR.code, reason);
sendGoAway = true;
@@ -1904,9 +1841,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
closed = CloseState.CLOSING;
zeroStreamsAction = null;
failure = cause = new TimeoutException("Session idle timeout expired");
- break;
}
- default:
+ default ->
{
if (LOG.isDebugEnabled())
LOG.debug("Already closed, ignored idle timeout for {}", HTTP2Session.this);
@@ -1937,22 +1873,19 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
GoAwayFrame goAwayFrame;
Throwable cause;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
- case LOCALLY_CLOSED:
- case REMOTELY_CLOSED:
+ case NOT_CLOSED, LOCALLY_CLOSED, REMOTELY_CLOSED ->
{
// Send another GOAWAY with the error code.
goAwaySent = goAwayFrame = newGoAwayFrame(error, reason);
closed = CloseState.CLOSING;
zeroStreamsAction = null;
failure = cause = toFailure(error, reason);
- break;
}
- default:
+ default ->
{
if (LOG.isDebugEnabled())
LOG.debug("Already closed, ignored session failure {}", HTTP2Session.this, failure);
@@ -1974,13 +1907,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private void onWriteFailure(Throwable x)
{
String reason = "write_failure";
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
- case NOT_CLOSED:
- case LOCALLY_CLOSED:
- case REMOTELY_CLOSED:
+ case NOT_CLOSED, LOCALLY_CLOSED, REMOTELY_CLOSED ->
{
closed = CloseState.CLOSING;
zeroStreamsAction = () ->
@@ -1989,9 +1920,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
terminate(goAwayFrame);
};
failure = x;
- break;
}
- default:
+ default ->
{
return;
}
@@ -2032,7 +1962,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
// but only one moves to CLOSED and runs the action.
Runnable action = null;
CompletableFuture future;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
long count = streamCount.get();
if (count > 0)
@@ -2046,34 +1976,27 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
switch (closed)
{
- case LOCALLY_CLOSED:
+ case LOCALLY_CLOSED ->
{
if (goAwaySent.isGraceful())
{
action = zeroStreamsAction;
zeroStreamsAction = null;
}
- break;
}
- case REMOTELY_CLOSED:
+ case REMOTELY_CLOSED ->
{
if (goAwaySent != null && goAwaySent.isGraceful())
{
action = zeroStreamsAction;
zeroStreamsAction = null;
}
- break;
}
- case CLOSING:
+ case CLOSING ->
{
closed = CloseState.CLOSED;
action = zeroStreamsAction;
zeroStreamsAction = null;
- break;
- }
- default:
- {
- break;
}
}
}
@@ -2114,7 +2037,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return streamId;
}
- private void newLocalStream(IStream.FrameList frameList, Promise promise, Stream.Listener listener)
+ private void newLocalStream(HTTP2Stream.FrameList frameList, Promise promise, Stream.Listener listener)
{
Slot slot = new Slot();
int currentStreamId = frameList.getStreamId();
@@ -2137,12 +2060,12 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private Stream newUpgradeStream(HeadersFrame frame, Stream.Listener listener, Consumer failFn)
{
int streamId;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
streamId = localStreamIds.getAndAdd(2);
HTTP2Session.this.onStreamCreated(streamId);
}
- IStream stream = HTTP2Session.this.createLocalStream(streamId, (MetaData.Request)frame.getMetaData(), x ->
+ HTTP2Stream stream = HTTP2Session.this.createLocalStream(streamId, (MetaData.Request)frame.getMetaData(), x ->
{
HTTP2Session.this.onStreamDestroyed(streamId);
failFn.accept(x);
@@ -2157,7 +2080,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private boolean newRemoteStream(int streamId)
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
switch (closed)
{
@@ -2201,7 +2124,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
MetaData.Request request = extractMetaDataRequest(frames.get(0));
if (request == null)
return false;
- IStream stream = HTTP2Session.this.createLocalStream(streamId, request, promise::failed);
+ HTTP2Stream stream = HTTP2Session.this.createLocalStream(streamId, request, promise::failed);
if (stream == null)
return false;
@@ -2241,7 +2164,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private int reserveSlot(Slot slot, int streamId, Consumer fail)
{
Throwable failure = null;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
if (closed == CloseState.NOT_CLOSED)
{
@@ -2267,7 +2190,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private void freeSlot(Slot slot, int streamId)
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
slots.remove(slot);
}
@@ -2296,7 +2219,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
while (true)
{
List entries;
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
if (flushing == null)
flushing = thread;
@@ -2324,7 +2247,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
@Override
public String toString()
{
- try (AutoLock l = lock.lock())
+ try (AutoLock ignored = lock.lock())
{
return String.format("state=[streams=%d,%s,goAwayRecv=%s,goAwaySent=%s,failure=%s]",
streamCount.get(),
@@ -2362,4 +2285,59 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
return false;
}
}
+
+ /**
+ * @implNote This class needs an extra reference counter because it needs to
+ * open the flow control window when the application releases this instance.
+ * Imagine a network buffer with 2 DATA frames: this will create 2 Data
+ * objects, which will be passed to the application. The network buffer is
+ * now retained 3 times (1 time for the network usage, and 1 time for each
+ * Data object).
+ * When the application releases the first Data object, the flow control
+ * window should be opened immediately for the length of that Data object,
+ * so the implementation cannot rely on delegating the call to release()
+ * to the network buffer, because the network buffer will still be retained.
+ * Furthermore, the flow control logic must be executed only once, while
+ * the Data object release() method may be invoked multiple times (since
+ * it may be additionally retained, for example when converted to a Chunk).
+ * The solution is to have an additional reference counter for the objects
+ * of this class, that allows to invoke the flow control logic only once,
+ * and only when all retains performed on an instance have been released.
+ */
+ private class StreamData extends Stream.Data
+ {
+ private final ReferenceCounter counter = new ReferenceCounter();
+ private final Stream.Data data;
+ private final HTTP2Stream stream;
+ private final int flowControlLength;
+
+ private StreamData(Stream.Data data, HTTP2Stream stream, int flowControlLength)
+ {
+ super(data.frame());
+ this.data = data;
+ this.stream = stream;
+ this.flowControlLength = flowControlLength;
+ }
+
+ @Override
+ public void retain()
+ {
+ counter.retain();
+ data.retain();
+ }
+
+ @Override
+ public boolean release()
+ {
+ data.release();
+ boolean result = counter.release();
+ if (result)
+ {
+ notIdle();
+ stream.notIdle();
+ flowControl.onDataConsumed(HTTP2Session.this, stream, flowControlLength);
+ }
+ return result;
+ }
+ }
}
diff --git a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Stream.java b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Stream.java
index ed32f9066f4..07eb4803f2a 100644
--- a/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Stream.java
+++ b/jetty-core/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/internal/HTTP2Stream.java
@@ -13,11 +13,16 @@
package org.eclipse.jetty.http2.internal;
+import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.nio.channels.WritePendingException;
import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Deque;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
@@ -30,8 +35,6 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.CloseState;
-import org.eclipse.jetty.http2.ISession;
-import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.FailureFrame;
@@ -39,31 +42,31 @@ import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.StreamFrame;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.io.CyclicTimeouts;
import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.util.Attachable;
import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.MathUtils;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.thread.AutoLock;
-import org.eclipse.jetty.util.thread.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class HTTP2Stream implements IStream, Callback, Dumpable, CyclicTimeouts.Expirable
+public class HTTP2Stream implements Stream, Attachable, Closeable, Callback, Dumpable, CyclicTimeouts.Expirable
{
private static final Logger LOG = LoggerFactory.getLogger(HTTP2Stream.class);
private final AutoLock lock = new AutoLock();
- private Deque dataQueue;
+ private Deque dataQueue;
private final AtomicReference