From 9a8e135b5adace941e826888f76ff9c7d6ca0223 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 15 Dec 2023 13:45:24 +0530 Subject: [PATCH 01/46] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- ballerina/CompilerPlugin.toml | 2 +- ballerina/Dependencies.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index c0b542a51c..1a999b248b 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "http" -version = "2.10.4" +version = "2.10.5" authors = ["Ballerina"] keywords = ["http", "network", "service", "listener", "client"] repository = "https://github.com/ballerina-platform/module-ballerina-http" @@ -16,8 +16,8 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "http-native" -version = "2.10.4" -path = "../native/build/libs/http-native-2.10.4.jar" +version = "2.10.5" +path = "../native/build/libs/http-native-2.10.5-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index f281c204da..d169504d52 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "http-compiler-plugin" class = "io.ballerina.stdlib.http.compiler.HttpCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/http-compiler-plugin-2.10.4.jar" +path = "../compiler-plugin/build/libs/http-compiler-plugin-2.10.5-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index f17306eb50..afcb80ecc4 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -76,7 +76,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.10.4" +version = "2.10.5" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, From e1c88f8d8cd4d6353ff78d3c2bb6c859d6fec42f Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 2 Jan 2024 22:05:49 +0530 Subject: [PATCH 02/46] Add stream goaway fix --- .../sender/http2/Http2ClientChannel.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 53af3e0e7c..c3c6d0dcfb 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -21,6 +21,7 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contractimpl.common.HttpRoute; import io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2MessageStateContext; +import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.handler.codec.http2.Http2Connection; @@ -293,6 +294,23 @@ public void onStreamClosed(Http2Stream stream) { http2ConnectionManager.returnClientChannel(httpRoute, http2ClientChannel); } } + + @Override + public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { + http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { + if (streamId > lastStreamId) { + http2ClientChannel.removeInFlightMessage(streamId); + activeStreams.decrementAndGet(); + http2ClientChannel.getDataEventListeners().forEach( + dataEventListener -> dataEventListener.onStreamClose(streamId)); + Http2MessageStateContext messageStateContext = + outboundMsgHolder.getRequest().getHttp2MessageStateContext(); + if (messageStateContext != null) { + messageStateContext.getSenderState().handleConnectionClose(outboundMsgHolder); + } + } + }); + } } void removeFromConnectionPool() { From 8f4cea490b9e40ceb00bf49cbeea604bc912671d Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 3 Jan 2024 09:37:54 +0530 Subject: [PATCH 03/46] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- ballerina/CompilerPlugin.toml | 2 +- ballerina/Dependencies.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 82ec499f11..6505a267e0 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "http" -version = "2.10.5" +version = "2.10.6" authors = ["Ballerina"] keywords = ["http", "network", "service", "listener", "client"] repository = "https://github.com/ballerina-platform/module-ballerina-http" @@ -16,8 +16,8 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "http-native" -version = "2.10.5" -path = "../native/build/libs/http-native-2.10.5.jar" +version = "2.10.6" +path = "../native/build/libs/http-native-2.10.6-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 6229522ec1..1172e0bbc2 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "http-compiler-plugin" class = "io.ballerina.stdlib.http.compiler.HttpCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/http-compiler-plugin-2.10.5.jar" +path = "../compiler-plugin/build/libs/http-compiler-plugin-2.10.6-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index afcb80ecc4..321c5e85c0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -76,7 +76,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.10.5" +version = "2.10.6" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, From 1da43d0bab4bdcd6979a822983f2c47a7927be73 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 5 Jan 2024 00:55:49 +0530 Subject: [PATCH 04/46] Remove client channel after goaway --- .../transport/contractimpl/sender/http2/Http2ClientChannel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index c3c6d0dcfb..2cc5dcebf0 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -297,6 +297,7 @@ public void onStreamClosed(Http2Stream stream) { @Override public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { + http2ConnectionManager.removeClientChannel(httpRoute, http2ClientChannel); http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { if (streamId > lastStreamId) { http2ClientChannel.removeInFlightMessage(streamId); From 5a76cb731d08e1e3cb8657347175308f19980857 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 5 Jan 2024 00:55:56 +0530 Subject: [PATCH 05/46] Add native test cases --- .../RemoteChannelCloseWithSSLError.java | 2 +- .../http2/goaway/GoAwayTestUtils.java | 23 +++ ...erverGoAwayMultipleStreamScenarioTest.java | 145 ++++++++++++++++++ ...pServerGoAwaySingleStreamScenarioTest.java | 99 ++++++++++++ ...rGoAwayWhileReceivingBodyScenarioTest.java | 96 ++++++++++++ ...verSendRequestAfterGoAwayScenarioTest.java | 128 ++++++++++++++++ .../Http2TcpServerSuccessScenarioTest.java | 102 ++++++++++++ native/src/test/resources/testng.xml | 5 + 8 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/clientchannelclose/RemoteChannelCloseWithSSLError.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/clientchannelclose/RemoteChannelCloseWithSSLError.java index 102b4ca711..70ddf3d5d7 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/clientchannelclose/RemoteChannelCloseWithSSLError.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/clientchannelclose/RemoteChannelCloseWithSSLError.java @@ -70,7 +70,7 @@ public void setup() throws InterruptedException { h2PriorOnClient = getTestHttp2Client(connectorFactory, true); } - //TODO:Change the assertion state once the issue https://githubcom/ballerina-platform/ballerina-lang/issues/17539 + //TODO:Change the assertion state once the issue https://github.com/ballerina-platform/ballerina-lang/issues/17539 // is fixed. @Test public void testRemoteChannelClose() { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java new file mode 100644 index 0000000000..d4fcce3d3c --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java @@ -0,0 +1,23 @@ +package io.ballerina.stdlib.http.transport.http2.goaway; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; +import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; +import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; + +public class GoAwayTestUtils { + + public static HttpClientConnector setupHttp2PriorKnowledgeClient() { + HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); + TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); + SenderConfiguration senderConfiguration = new SenderConfiguration(); + senderConfiguration.setScheme(Constants.HTTP_SCHEME); + senderConfiguration.setHttpVersion(Constants.HTTP_2_0); + senderConfiguration.setForceHttp2(true); + return connectorFactory.createHttpClientConnector( + HttpConnectorUtil.getTransportProperties(transportsConfiguration), senderConfiguration); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java new file mode 100644 index 0000000000..3f7557d549 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -0,0 +1,145 @@ +package io.ballerina.stdlib.http.transport.http2.goaway; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; +import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; +import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsNoOrder; +import static org.testng.Assert.fail; + +public class Http2TcpServerGoAwayMultipleStreamScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayMultipleStreamScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + startTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { + HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage4 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(4); + DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); + responseFuture1.setHttpConnectorListener(msgListener1); + DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); + responseFuture2.setHttpConnectorListener(msgListener2); + DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); + responseFuture3.setHttpConnectorListener(msgListener3); + DefaultHttpConnectorListener msgListener4 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture4 = h2ClientWithPriorKnowledge.send(httpCarbonMessage4); + responseFuture4.setHttpConnectorListener(msgListener4); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture1.sync(); + responseFuture2.sync(); + responseFuture3.sync(); + responseFuture4.sync(); + HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); + HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); + HttpCarbonMessage response3 = msgListener3.getHttpResponseMessage(); + HttpCarbonMessage response4 = msgListener4.getHttpResponseMessage(); + Object responseValOrError1 = response1 == null ? responseFuture1.getStatus().getCause().getMessage() : + response1.getHttpContent().content().toString(CharsetUtil.UTF_8); + Object responseValOrError2 = response2 == null ? responseFuture2.getStatus().getCause().getMessage() : + response2.getHttpContent().content().toString(CharsetUtil.UTF_8); + Object responseValOrError3 = response3 == null ? responseFuture3.getStatus().getCause().getMessage() : + response3.getHttpContent().content().toString(CharsetUtil.UTF_8); + Object responseValOrError4 = response4 == null ? responseFuture4.getStatus().getCause().getMessage() : + response4.getHttpContent().content().toString(CharsetUtil.UTF_8); + assertEqualsNoOrder(List.of(responseValOrError1, responseValOrError2, responseValOrError3, + responseValOrError4), List.of("hello worl3", "hello worl5", "hello worl7", + REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void startTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + + while (true) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendGoAwayForASingleStreamInAMultipleStreamScenario(outputStream); + break; + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + + private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + LOGGER.info("Wrote settings frame"); + outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); + LOGGER.info("Writing settings frame with ack"); + outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); + Thread.sleep(100); + LOGGER.info("Writing headers frame to stream 3"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + Thread.sleep(100); + LOGGER.info("Writing headers frame to stream 5"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x05, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + LOGGER.info("Writing data frame to stream 5"); + outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x35}); + LOGGER.info("Writing a go away frame with max frame number 7"); + outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0b}); + Thread.sleep(100); + LOGGER.info("Writing headers frame to stream 9"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x09, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + LOGGER.info("Writing data frame to stream 9"); + outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x39}); + Thread.sleep(100); + LOGGER.info("Writing headers frame to stream 7"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x07, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + LOGGER.info("Writing data frame to stream 7"); + outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x37}); + Thread.sleep(100); + LOGGER.info("Writing data frame to stream 3"); + outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x33}); + Thread.sleep(100); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java new file mode 100644 index 0000000000..29c01a8197 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -0,0 +1,99 @@ +package io.ballerina.stdlib.http.transport.http2.goaway; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; +import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; +import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +public class Http2TcpServerGoAwaySingleStreamScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwaySingleStreamScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + startTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testGoAwayWhenReceivingHeadersForASingleStream() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + Throwable responseError = responseFuture.getStatus().getCause(); + if (responseError != null) { + assertEquals(responseError.getMessage(), REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + } else { + fail("Expected error not received"); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void startTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + + while (true) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendGoAwayForASingleStream(outputStream); + break; + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + + private static void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + LOGGER.info("Wrote settings frame"); + outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); + Thread.sleep(100); + LOGGER.info("Writing settings frame with ack"); + outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); + Thread.sleep(100); + LOGGER.info("Writing a go away frame to stream 3"); + outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}); + Thread.sleep(100); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java new file mode 100644 index 0000000000..4269ecfc65 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -0,0 +1,96 @@ +package io.ballerina.stdlib.http.transport.http2.goaway; + +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +public class Http2TcpServerGoAwayWhileReceivingBodyScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + startTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testGoAwayWhenReceivingBodyForASingleStream() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); + if (content != null) { + assertEquals(content.decoderResult().cause().getMessage(), REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + } else { + fail("Expected http content"); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void startTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + while (true) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendGoAwayAfterSendingHeadersForASingleStream(outputStream); + break; + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + + private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + LOGGER.info("Wrote settings frame"); + outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); + Thread.sleep(100); + LOGGER.info("Writing settings frame with ack"); + outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); + Thread.sleep(100); + LOGGER.info("Writing headers frame with status 200"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + Thread.sleep(100); + LOGGER.info("Writing a go away frame to stream 3"); + outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}); + Thread.sleep(100); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java new file mode 100644 index 0000000000..c8f062ddba --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -0,0 +1,128 @@ +package io.ballerina.stdlib.http.transport.http2.goaway; + +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerSendRequestAfterGoAwayScenarioTest.class); + + private HttpClientConnector h2ClientWithPriorKnowledge; + private AtomicInteger numberOfConnections = new AtomicInteger(0); + + @BeforeClass + public void setup() throws InterruptedException { + startTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testNewRequestAfterGoAwayReceivedScenario() { + HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch1 = new CountDownLatch(1); + CountDownLatch latch2 = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch1); + HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); + responseFuture1.setHttpConnectorListener(msgListener1); + latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture1.sync(); + DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch2); + HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); + responseFuture2.setHttpConnectorListener(msgListener2); + latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture2.sync(); + Throwable responseError = responseFuture1.getStatus().getCause(); + if (responseError != null) { + assertEquals(responseError.getMessage(), REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + } else { + fail("Expected error not received"); + } + HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); + assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world"); + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void startTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + while (true) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + if (numberOfConnections.get() == 0) { + sendGoAway(outputStream); + numberOfConnections.set(1); + } else if (numberOfConnections.get() == 1) { + sendSuccessfulResponse(outputStream); + break; + } else { + break; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + + private static void sendGoAway(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + LOGGER.info("Wrote settings frame"); + outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); + Thread.sleep(100); + LOGGER.info("Writing settings frame with ack"); + outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); + Thread.sleep(100); + LOGGER.info("Writing a go away frame to stream 3"); + outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}); + Thread.sleep(100); + } + + private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + LOGGER.info("Wrote settings frame"); + outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); + Thread.sleep(100); + LOGGER.info("Writing settings frame with ack"); + outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); + Thread.sleep(100); + LOGGER.info("Writing headers frame with status 200"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + Thread.sleep(100); + LOGGER.info("Writing data frame with hello world"); + outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}); + Thread.sleep(100); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java new file mode 100644 index 0000000000..198f89336a --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java @@ -0,0 +1,102 @@ +package io.ballerina.stdlib.http.transport.http2.goaway; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; +import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; +import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsNoOrder; +import static org.testng.Assert.fail; + +public class Http2TcpServerSuccessScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerSuccessScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + startTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testSuccessfulConnection() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); + assertEquals(content.content().toString(CharsetUtil.UTF_8), "hello world"); + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void startTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + while (true) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendSuccessfulResponse(outputStream); + break; + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + LOGGER.info("Wrote settings frame"); + outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); + Thread.sleep(100); + LOGGER.info("Writing settings frame with ack"); + outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); + Thread.sleep(100); + LOGGER.info("Writing headers frame with status 200"); + outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); + Thread.sleep(100); + LOGGER.info("Writing data frame with hello world"); + outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}); + Thread.sleep(100); + } +} diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index 6f7dbcd813..3481cb5d08 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -154,6 +154,11 @@ + + + + + From 2d14863b0812b1d662fa5c996b0d047baeb1e261 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 5 Jan 2024 01:03:51 +0530 Subject: [PATCH 06/46] Add license headers and comments --- .../http2/goaway/GoAwayTestUtils.java | 18 +++++++++++ ...erverGoAwayMultipleStreamScenarioTest.java | 29 ++++++++++++----- ...pServerGoAwaySingleStreamScenarioTest.java | 27 ++++++++++++---- ...rGoAwayWhileReceivingBodyScenarioTest.java | 21 ++++++++++++ ...verSendRequestAfterGoAwayScenarioTest.java | 22 +++++++++++++ .../Http2TcpServerSuccessScenarioTest.java | 32 ++++++++++++------- 6 files changed, 124 insertions(+), 25 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java index d4fcce3d3c..c40ca5383e 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.http.transport.http2.goaway; import io.ballerina.stdlib.http.transport.contract.Constants; diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index 3f7557d549..71aef9dcb8 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -1,14 +1,26 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.http.transport.http2.goaway; -import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; -import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; -import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; -import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; -import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; @@ -28,10 +40,11 @@ import java.util.concurrent.TimeUnit; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; -import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEqualsNoOrder; -import static org.testng.Assert.fail; +/** + * This contains a test case where the tcp server sends a GoAway for a stream in a multiple stream scenario. + */ public class Http2TcpServerGoAwayMultipleStreamScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayMultipleStreamScenarioTest.class); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java index 29c01a8197..57476afe79 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -1,14 +1,26 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.http.transport.http2.goaway; -import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; -import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; -import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; -import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; -import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; @@ -29,6 +41,9 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; +/** + * This contains a test case where the tcp server sends a GoAway for a single request. + */ public class Http2TcpServerGoAwaySingleStreamScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwaySingleStreamScenarioTest.class); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index 4269ecfc65..e54debd8b8 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.http.transport.http2.goaway; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -24,6 +42,9 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; +/** + * This contains a test case where the tcp server sends a GoAway while client receives the body. + */ public class Http2TcpServerGoAwayWhileReceivingBodyScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.class); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index c8f062ddba..8ab41b072f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.http.transport.http2.goaway; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -25,6 +43,10 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; +/** + * This contains a test case where the client sends a request after receiving a GoAway. + * This tests whether there is a new connection opened from the client. + */ public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerSendRequestAfterGoAwayScenarioTest.class); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java index 198f89336a..9f982b5d2f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java @@ -1,14 +1,26 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.http.transport.http2.goaway; -import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; -import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; -import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; -import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; -import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; @@ -18,22 +30,20 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertEqualsNoOrder; -import static org.testng.Assert.fail; +/** + * This contains a test case where the tcp server sends a successful response. + */ public class Http2TcpServerSuccessScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerSuccessScenarioTest.class); From 505544b698f7e7a56fe9fa3635f42cb0d2f7d7ec Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 5 Jan 2024 10:14:46 +0530 Subject: [PATCH 07/46] Fix line lengths --- .../http2/goaway/GoAwayTestUtils.java | 27 +++++++ ...erverGoAwayMultipleStreamScenarioTest.java | 70 ++++++++++--------- ...pServerGoAwaySingleStreamScenarioTest.java | 28 ++++---- ...rGoAwayWhileReceivingBodyScenarioTest.java | 40 ++++++----- ...verSendRequestAfterGoAwayScenarioTest.java | 55 ++++++++------- .../Http2TcpServerSuccessScenarioTest.java | 27 +++---- 6 files changed, 142 insertions(+), 105 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java index c40ca5383e..c90debb0ec 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java @@ -28,6 +28,33 @@ public class GoAwayTestUtils { + public static final int SLEEP_TIME = 100; + + public static final byte[] SETTINGS_FRAME = new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}; + public static final byte[] SETTINGS_FRAME_WITH_ACK = + new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}; + public static final byte[] HEADER_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, + (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}; + public static final byte[] HEADER_FRAME_STREAM_05 = new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x05, + (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}; + public static final byte[] HEADER_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x07, + (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}; + public static final byte[] HEADER_FRAME_STREAM_09 = new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x09, + (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}; + public static final byte[] DATA_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x33}; + public static final byte[] DATA_FRAME_STREAM_05 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x35}; + public static final byte[] DATA_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x37}; + public static final byte[] DATA_FRAME_STREAM_09 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x39}; + public static final byte[] GO_AWAY_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}; + public static final byte[] GO_AWAY_FRAME_MAX_STREAM_07 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0b}; + public static HttpClientConnector setupHttp2PriorKnowledgeClient() { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index 71aef9dcb8..2afae06cb4 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -18,6 +18,7 @@ package io.ballerina.stdlib.http.transport.http2.goaway; +import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; @@ -39,7 +40,18 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_MAX_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; import static org.testng.Assert.assertEqualsNoOrder; /** @@ -94,8 +106,8 @@ private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { Object responseValOrError4 = response4 == null ? responseFuture4.getStatus().getCause().getMessage() : response4.getHttpContent().content().toString(CharsetUtil.UTF_8); assertEqualsNoOrder(List.of(responseValOrError1, responseValOrError2, responseValOrError3, - responseValOrError4), List.of("hello worl3", "hello worl5", "hello worl7", - REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); + responseValOrError4), List.of("hello world3", "hello world5", "hello world7", + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } @@ -115,44 +127,34 @@ private void startTcpServer(int port) { sendGoAwayForASingleStreamInAMultipleStreamScenario(outputStream); break; } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } }).start(); } - private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputStream outputStream) throws IOException, InterruptedException { + private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputStream outputStream) + throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 - LOGGER.info("Wrote settings frame"); - outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); - LOGGER.info("Writing settings frame with ack"); - outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); - Thread.sleep(100); - LOGGER.info("Writing headers frame to stream 3"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - Thread.sleep(100); - LOGGER.info("Writing headers frame to stream 5"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x05, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - LOGGER.info("Writing data frame to stream 5"); - outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x35}); - LOGGER.info("Writing a go away frame with max frame number 7"); - outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0b}); - Thread.sleep(100); - LOGGER.info("Writing headers frame to stream 9"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x09, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - LOGGER.info("Writing data frame to stream 9"); - outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x39}); - Thread.sleep(100); - LOGGER.info("Writing headers frame to stream 7"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x07, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - LOGGER.info("Writing data frame to stream 7"); - outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x37}); - Thread.sleep(100); - LOGGER.info("Writing data frame to stream 3"); - outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x33}); - Thread.sleep(100); + outputStream.write(SETTINGS_FRAME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_05); + outputStream.write(DATA_FRAME_STREAM_05); + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_07); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_09); + outputStream.write(DATA_FRAME_STREAM_09); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_07); + outputStream.write(DATA_FRAME_STREAM_07); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java index 57476afe79..0403077c5e 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -18,6 +18,7 @@ package io.ballerina.stdlib.http.transport.http2.goaway; +import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; @@ -37,7 +38,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -67,7 +71,8 @@ private void testGoAwayWhenReceivingHeadersForASingleStream() { responseFuture.sync(); Throwable responseError = responseFuture.getStatus().getCause(); if (responseError != null) { - assertEquals(responseError.getMessage(), REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + assertEquals(responseError.getMessage(), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } else { fail("Expected error not received"); } @@ -90,25 +95,22 @@ private void startTcpServer(int port) { sendGoAwayForASingleStream(outputStream); break; } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } }).start(); } private static void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 - LOGGER.info("Wrote settings frame"); - outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); - Thread.sleep(100); - LOGGER.info("Writing settings frame with ack"); - outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); - Thread.sleep(100); - LOGGER.info("Writing a go away frame to stream 3"); - outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}); - Thread.sleep(100); + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index e54debd8b8..2610ef88cd 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -18,6 +18,7 @@ package io.ballerina.stdlib.http.transport.http2.goaway; +import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; @@ -38,7 +39,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -47,7 +52,8 @@ */ public class Http2TcpServerGoAwayWhileReceivingBodyScenarioTest { - private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.class); + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; @BeforeClass @@ -68,7 +74,8 @@ private void testGoAwayWhenReceivingBodyForASingleStream() { responseFuture.sync(); HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); if (content != null) { - assertEquals(content.decoderResult().cause().getMessage(), REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + assertEquals(content.decoderResult().cause().getMessage(), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); } else { fail("Expected http content"); } @@ -90,28 +97,25 @@ private void startTcpServer(int port) { sendGoAwayAfterSendingHeadersForASingleStream(outputStream); break; } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } }).start(); } - private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream outputStream) + throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 - LOGGER.info("Wrote settings frame"); - outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); - Thread.sleep(100); - LOGGER.info("Writing settings frame with ack"); - outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); - Thread.sleep(100); - LOGGER.info("Writing headers frame with status 200"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - Thread.sleep(100); - LOGGER.info("Writing a go away frame to stream 3"); - outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}); - Thread.sleep(100); + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 8ab41b072f..506f8b0f7c 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -18,6 +18,7 @@ package io.ballerina.stdlib.http.transport.http2.goaway; +import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; @@ -39,7 +40,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -49,7 +55,8 @@ */ public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { - private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerSendRequestAfterGoAwayScenarioTest.class); + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2TcpServerSendRequestAfterGoAwayScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; private AtomicInteger numberOfConnections = new AtomicInteger(0); @@ -79,12 +86,13 @@ private void testNewRequestAfterGoAwayReceivedScenario() { responseFuture2.sync(); Throwable responseError = responseFuture1.getStatus().getCause(); if (responseError != null) { - assertEquals(responseError.getMessage(), REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + assertEquals(responseError.getMessage(), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } else { fail("Expected error not received"); } HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); - assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world"); + assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } @@ -110,41 +118,34 @@ private void startTcpServer(int port) { break; } } catch (Exception e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error(e.getMessage()); } }).start(); } private static void sendGoAway(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 - LOGGER.info("Wrote settings frame"); - outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); - Thread.sleep(100); - LOGGER.info("Writing settings frame with ack"); - outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); - Thread.sleep(100); - LOGGER.info("Writing a go away frame to stream 3"); - outputStream.write(new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}); - Thread.sleep(100); + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); } private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 - LOGGER.info("Wrote settings frame"); - outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); - Thread.sleep(100); - LOGGER.info("Writing settings frame with ack"); - outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); - Thread.sleep(100); - LOGGER.info("Writing headers frame with status 200"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - Thread.sleep(100); - LOGGER.info("Writing data frame with hello world"); - outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}); - Thread.sleep(100); + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java index 9f982b5d2f..217385dd51 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java @@ -39,6 +39,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; /** @@ -66,7 +71,7 @@ private void testSuccessfulConnection() { latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); responseFuture.sync(); HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); - assertEquals(content.content().toString(CharsetUtil.UTF_8), "hello world"); + assertEquals(content.content().toString(CharsetUtil.UTF_8), "hello world3"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } @@ -96,17 +101,13 @@ private void startTcpServer(int port) { private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 - LOGGER.info("Wrote settings frame"); - outputStream.write(new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}); - Thread.sleep(100); - LOGGER.info("Writing settings frame with ack"); - outputStream.write(new byte[]{0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}); - Thread.sleep(100); - LOGGER.info("Writing headers frame with status 200"); - outputStream.write(new byte[]{0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x03, (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}); - Thread.sleep(100); - LOGGER.info("Writing data frame with hello world"); - outputStream.write(new byte[]{0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}); - Thread.sleep(100); + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); } } From 3f371152b988d04f579e94de362a994423ab045a Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Sun, 7 Jan 2024 23:17:28 +0530 Subject: [PATCH 08/46] Add rst test cases --- ...erverGoAwayMultipleStreamScenarioTest.java | 55 +++--- ...pServerGoAwaySingleStreamScenarioTest.java | 161 ++++++++++++++++++ ...rGoAwayWhileReceivingBodyScenarioTest.java | 40 ++--- ...verGoAwayWhileSendingBodyScenarioTest.java | 121 +++++++++++++ ...rRSTStreamFrameForMultipleStreamsTest.java | 147 ++++++++++++++++ ...verRSTStreamFrameForSingleStreamTest.java} | 57 +++---- ...ameWhenReadingBodyForSingleStreamTest.java | 119 +++++++++++++ ...verSendRequestAfterGoAwayScenarioTest.java | 26 +-- .../Http2TcpServerSuccessScenarioTest.java | 38 ++--- .../TestUtils.java} | 14 +- native/src/test/resources/testng.xml | 12 +- 11 files changed, 676 insertions(+), 114 deletions(-) rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/{goaway => frameleveltests}/Http2TcpServerGoAwayMultipleStreamScenarioTest.java (75%) create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/{goaway => frameleveltests}/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java (74%) create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/{goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java => frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java} (63%) create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/{goaway => frameleveltests}/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java (85%) rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/{goaway => frameleveltests}/Http2TcpServerSuccessScenarioTest.java (73%) rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/{goaway/GoAwayTestUtils.java => frameleveltests/TestUtils.java} (85%) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java similarity index 75% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index 2afae06cb4..010671f13e 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.stdlib.http.transport.http2.goaway; +package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -40,18 +40,19 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_09; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_MAX_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_09; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; import static org.testng.Assert.assertEqualsNoOrder; /** @@ -64,8 +65,8 @@ public class Http2TcpServerGoAwayMultipleStreamScenarioTest { @BeforeClass public void setup() throws InterruptedException { - startTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); } @Test @@ -113,22 +114,20 @@ private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { } } - private void startTcpServer(int port) { + private void runTcpServer(int port) { new Thread(() -> { ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); - - while (true) { - Socket clientSocket = serverSocket.accept(); - LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); - try (OutputStream outputStream = clientSocket.getOutputStream()) { - sendGoAwayForASingleStreamInAMultipleStreamScenario(outputStream); - break; - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendGoAwayForASingleStreamInAMultipleStreamScenario(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -148,13 +147,15 @@ private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputSt outputStream.write(DATA_FRAME_STREAM_05); outputStream.write(GO_AWAY_FRAME_MAX_STREAM_07); Thread.sleep(SLEEP_TIME); + // Sending the frames for higher streams to check whether client correctly ignores them. outputStream.write(HEADER_FRAME_STREAM_09); outputStream.write(DATA_FRAME_STREAM_09); Thread.sleep(SLEEP_TIME); + // Sending the frames for lower streams to check whether client correctly accepts them. outputStream.write(HEADER_FRAME_STREAM_07); outputStream.write(DATA_FRAME_STREAM_07); Thread.sleep(SLEEP_TIME); outputStream.write(DATA_FRAME_STREAM_03); - Thread.sleep(SLEEP_TIME); + Thread.sleep(END_SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java new file mode 100644 index 0000000000..61e1f58950 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.stdlib.http.transport.http2.frameleveltests; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +/** + * This contains a test case where the tcp server sends a GoAway for a single request. + */ +public class Http2TcpServerGoAwaySingleStreamScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwaySingleStreamScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeMethod + public void setup(Method method) throws InterruptedException { + if (method.getName().equals("testGoAwayWhenReceivingHeadersForASingleStream")) { + runTcpServer(TestUtil.HTTP_SERVER_PORT, 1); + } else { + runTcpServer(TestUtil.HTTP_SERVER_PORT, 2); + } + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test (description = "In this, server sends headers and data for the accepted stream") + private void testGoAwayWhenReceivingHeadersForASingleStream() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); + assertEquals(content.content().toString(CharsetUtil.UTF_8), "hello world3"); + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + @Test (description = "In this, server exits without sending the headers and data for the accepted stream as well") + private void testGoAwayAndServerExitWhenReceivingHeadersForASingleStream() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + Throwable responseError = responseFuture.getStatus().getCause(); + if (responseError != null) { + assertEquals(responseError.getMessage(), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + } else { + fail("Expected error not received"); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void runTcpServer(int port, int option) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + if (option == 1) { + sendGoAwayForASingleStream(outputStream); + } else { + sendGoAwayAndExitForASingleStream(outputStream); + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + private static void sendGoAwayAndExitForASingleStream(OutputStream outputStream) + throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_03); + // Once the sleep time elapses, channel inactive of client gets fired. + Thread.sleep(SLEEP_TIME); + } + + private static void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_03); + Thread.sleep(END_SLEEP_TIME); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java similarity index 74% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index 2610ef88cd..e6e62bee99 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.stdlib.http.transport.http2.goaway; +package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -39,11 +39,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_01; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -58,8 +59,8 @@ public class Http2TcpServerGoAwayWhileReceivingBodyScenarioTest { @BeforeClass public void setup() throws InterruptedException { - startTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); } @Test @@ -84,21 +85,20 @@ private void testGoAwayWhenReceivingBodyForASingleStream() { } } - private void startTcpServer(int port) { + private void runTcpServer(int port) { new Thread(() -> { ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); - while (true) { - Socket clientSocket = serverSocket.accept(); - LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); - try (OutputStream outputStream = clientSocket.getOutputStream()) { - sendGoAwayAfterSendingHeadersForASingleStream(outputStream); - break; - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendGoAwayAfterSendingHeadersForASingleStream(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -115,7 +115,7 @@ private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream o Thread.sleep(SLEEP_TIME); outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_03); - Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_01); + Thread.sleep(END_SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java new file mode 100644 index 0000000000..0a8a5fa7c6 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.stdlib.http.transport.http2.frameleveltests; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_01; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +/** + * This contains a test case where the tcp server sends a GoAway while client sends the body. + */ +public class Http2TcpServerGoAwayWhileSendingBodyScenarioTest { + + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2TcpServerGoAwayWhileSendingBodyScenarioTest.class); + + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testGoAwayWhenSendingBodyForASingleStream() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "hello world"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); + if (content != null) { + assertEquals(content.decoderResult().cause().getMessage(), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + } else { + fail("Expected http content"); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void runTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendGoAwayWhileSendingBodyForASingleStream(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + private static void sendGoAwayWhileSendingBodyForASingleStream(OutputStream outputStream) + throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); +// outputStream.write(HEADER_FRAME_STREAM_03); +// Thread.sleep(SLEEP_TIME); + outputStream.write(GO_AWAY_FRAME_STREAM_01); + Thread.sleep(END_SLEEP_TIME); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java new file mode 100644 index 0000000000..e56644dd3e --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.stdlib.http.transport.http2.frameleveltests; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +/** + * This contains a test case where the tcp server sends a successful response. + */ +public class Http2TcpServerRSTStreamFrameForMultipleStreamsTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameForMultipleStreamsTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testRSTStreamFrameForSingleStream() { + HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(3); + DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); + responseFuture1.setHttpConnectorListener(msgListener1); + DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); + responseFuture2.setHttpConnectorListener(msgListener2); + DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); + responseFuture3.setHttpConnectorListener(msgListener3); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture1.sync(); + responseFuture2.sync(); + responseFuture3.sync(); + Throwable throwable = responseFuture1.getStatus().getCause(); + if (throwable != null) { + assertEquals(throwable.getMessage(), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + } else { + fail("Expected an error"); + } + HttpContent content2 = msgListener2.getHttpResponseMessage().getHttpContent(); + assertEquals(content2.content().toString(CharsetUtil.UTF_8), "hello world5"); + HttpContent content3 = msgListener3.getHttpResponseMessage().getHttpContent(); + if (content3 != null) { + assertEquals(content3.decoderResult().cause().getMessage(), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + } else { + fail("Expected http content"); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void runTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendRSTStream(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + // This will send an RST_STREAM frame for stream 3 before sending headers and a successful response for + // stream 5 and an RST_STREAM frame for stream 7 after sending headers + private static void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(RST_STREAM_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_05); + outputStream.write(HEADER_FRAME_STREAM_07); + Thread.sleep(100); + outputStream.write(DATA_FRAME_STREAM_05); + outputStream.write(RST_STREAM_FRAME_STREAM_07); + Thread.sleep(END_SLEEP_TIME); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java similarity index 63% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index 0403077c5e..49188b2768 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.stdlib.http.transport.http2.goaway; +package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -38,29 +38,30 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; /** - * This contains a test case where the tcp server sends a GoAway for a single request. + * This contains a test case where the tcp server sends a successful response. */ -public class Http2TcpServerGoAwaySingleStreamScenarioTest { +public class Http2TcpServerRSTStreamFrameForSingleStreamTest { - private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwaySingleStreamScenarioTest.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameForSingleStreamTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; @BeforeClass public void setup() throws InterruptedException { - startTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); } @Test - private void testGoAwayWhenReceivingHeadersForASingleStream() { + private void testRSTStreamFrameForSingleStream() { HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { CountDownLatch latch = new CountDownLatch(1); @@ -69,34 +70,32 @@ private void testGoAwayWhenReceivingHeadersForASingleStream() { responseFuture.setHttpConnectorListener(msgListener); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); responseFuture.sync(); - Throwable responseError = responseFuture.getStatus().getCause(); - if (responseError != null) { - assertEquals(responseError.getMessage(), + Throwable throwable = responseFuture.getStatus().getCause(); + if (throwable != null) { + assertEquals(throwable.getMessage(), Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } else { - fail("Expected error not received"); + fail("Expected an error"); } } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } } - private void startTcpServer(int port) { + private void runTcpServer(int port) { new Thread(() -> { ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); - - while (true) { - Socket clientSocket = serverSocket.accept(); - LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); - try (OutputStream outputStream = clientSocket.getOutputStream()) { - sendGoAwayForASingleStream(outputStream); - break; - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendRSTStream(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -104,13 +103,13 @@ private void startTcpServer(int port) { }).start(); } - private static void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + private static void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME_WITH_ACK); Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_03); - Thread.sleep(SLEEP_TIME); + outputStream.write(RST_STREAM_FRAME_STREAM_03); + Thread.sleep(END_SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java new file mode 100644 index 0000000000..66d5914ae1 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.stdlib.http.transport.http2.frameleveltests; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +/** + * This contains a test case where the tcp server sends a successful response. + */ +public class Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + + @BeforeClass + public void setup() throws InterruptedException { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + } + + @Test + private void testRSTStreamFrameWhenReadingBodyForSingleStream() { + HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); + HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); + responseFuture.setHttpConnectorListener(msgListener); + latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture.sync(); + HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); + if (content != null) { + assertEquals(content.decoderResult().cause().getMessage(), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + } else { + fail("Expected http content"); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void runTcpServer(int port) { + new Thread(() -> { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendRSTStream(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + private static void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { + // Sending settings frame with HEADER_TABLE_SIZE=25700 + outputStream.write(SETTINGS_FRAME); + Thread.sleep(SLEEP_TIME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(RST_STREAM_FRAME_STREAM_03); + Thread.sleep(END_SLEEP_TIME); + } +} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java similarity index 85% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 506f8b0f7c..19b7f54c55 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.stdlib.http.transport.http2.goaway; +package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -40,12 +40,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.GO_AWAY_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -63,8 +64,8 @@ public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { @BeforeClass public void setup() throws InterruptedException { - startTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); } @Test @@ -98,7 +99,7 @@ private void testNewRequestAfterGoAwayReceivedScenario() { } } - private void startTcpServer(int port) { + private void runTcpServer(int port) { new Thread(() -> { ServerSocket serverSocket; try { @@ -121,6 +122,7 @@ private void startTcpServer(int port) { LOGGER.error(e.getMessage()); } } + serverSocket.close(); } catch (IOException e) { LOGGER.error(e.getMessage()); } @@ -134,7 +136,7 @@ private static void sendGoAway(OutputStream outputStream) throws IOException, In outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(GO_AWAY_FRAME_STREAM_03); - Thread.sleep(SLEEP_TIME); + Thread.sleep(END_SLEEP_TIME); } private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { @@ -146,6 +148,6 @@ private static void sendSuccessfulResponse(OutputStream outputStream) throws IOE outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); outputStream.write(DATA_FRAME_STREAM_03); - Thread.sleep(SLEEP_TIME); + Thread.sleep(END_SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java similarity index 73% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java index 217385dd51..a0dc863474 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.stdlib.http.transport.http2.goaway; +package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; @@ -39,11 +39,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.goaway.GoAwayTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; /** @@ -56,8 +57,8 @@ public class Http2TcpServerSuccessScenarioTest { @BeforeClass public void setup() throws InterruptedException { - startTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = GoAwayTestUtils.setupHttp2PriorKnowledgeClient(); + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); } @Test @@ -77,21 +78,20 @@ private void testSuccessfulConnection() { } } - private void startTcpServer(int port) { + private void runTcpServer(int port) { new Thread(() -> { ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); - while (true) { - Socket clientSocket = serverSocket.accept(); - LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); - try (OutputStream outputStream = clientSocket.getOutputStream()) { - sendSuccessfulResponse(outputStream); - break; - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + sendSuccessfulResponse(outputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } finally { + serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -108,6 +108,6 @@ private static void sendSuccessfulResponse(OutputStream outputStream) throws IOE outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); outputStream.write(DATA_FRAME_STREAM_03); - Thread.sleep(SLEEP_TIME); + Thread.sleep(END_SLEEP_TIME); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java similarity index 85% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java index c90debb0ec..a30a3d11c0 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/goaway/GoAwayTestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.stdlib.http.transport.http2.goaway; +package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; @@ -26,9 +26,13 @@ import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; -public class GoAwayTestUtils { +/** + * This contains the utils required for frame level tests. + */ +public class TestUtils { public static final int SLEEP_TIME = 100; + public static final int END_SLEEP_TIME = 5000; public static final byte[] SETTINGS_FRAME = new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}; @@ -50,10 +54,16 @@ public class GoAwayTestUtils { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x37}; public static final byte[] DATA_FRAME_STREAM_09 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x39}; + public static final byte[] GO_AWAY_FRAME_STREAM_01 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b}; public static final byte[] GO_AWAY_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}; public static final byte[] GO_AWAY_FRAME_MAX_STREAM_07 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0b}; + public static final byte[] RST_STREAM_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x02}; + public static final byte[] RST_STREAM_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x02}; public static HttpClientConnector setupHttp2PriorKnowledgeClient() { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index 3481cb5d08..2f73f51d38 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -154,11 +154,13 @@ - - - - - + + + + + + + From f3cd6f906e3d6a994697feb0abede72de13a78bf Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Sun, 7 Jan 2024 23:17:38 +0530 Subject: [PATCH 09/46] Add rst frame fix --- .../contractimpl/sender/http2/Http2ClientChannel.java | 3 ++- .../contractimpl/sender/http2/Http2TargetHandler.java | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 2cc5dcebf0..218efc1084 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -297,7 +297,8 @@ public void onStreamClosed(Http2Stream stream) { @Override public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - http2ConnectionManager.removeClientChannel(httpRoute, http2ClientChannel); + isExhausted.set(true); + removeFromConnectionPool(); http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { if (streamId > lastStreamId) { http2ClientChannel.removeInFlightMessage(streamId); diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java index 018a3805e9..14d1f56921 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java @@ -265,8 +265,11 @@ private void onResetRead(Http2Reset http2Reset) { int streamId = http2Reset.getStreamId(); OutboundMsgHolder outboundMsgHolder = http2ClientChannel.getInFlightMessage(streamId); if (outboundMsgHolder != null) { - outboundMsgHolder.getResponseFuture() - .notifyHttpListener(new Exception("HTTP/2 stream " + streamId + " reset by the remote peer")); + Http2MessageStateContext messageStateContext = + outboundMsgHolder.getRequest().getHttp2MessageStateContext(); + if (messageStateContext != null) { + messageStateContext.getSenderState().handleConnectionClose(outboundMsgHolder); + } } } From 0f72561d648b6fec8ae7de80997c5a240eb1311f Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Sun, 7 Jan 2024 23:32:04 +0530 Subject: [PATCH 10/46] Fix line lengths --- .../Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java | 3 ++- ...ServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index e56644dd3e..3ee33f07ad 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -57,7 +57,8 @@ */ public class Http2TcpServerRSTStreamFrameForMultipleStreamsTest { - private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameForMultipleStreamsTest.class); + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameForMultipleStreamsTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; @BeforeClass diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index 66d5914ae1..09b643dbb0 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -53,7 +53,8 @@ */ public class Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest { - private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.class); + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; @BeforeClass From 40ded0075f9d3a3302f9bed4f74d2b056392c53f Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Mon, 8 Jan 2024 16:32:33 +0530 Subject: [PATCH 11/46] Fix test cases --- ...erverGoAwayMultipleStreamScenarioTest.java | 11 +- ...pServerGoAwaySingleStreamScenarioTest.java | 18 +-- ...rGoAwayWhileReceivingBodyScenarioTest.java | 11 +- ...verGoAwayWhileSendingBodyScenarioTest.java | 121 ------------------ ...rRSTStreamFrameForMultipleStreamsTest.java | 55 ++++++-- ...rverRSTStreamFrameForSingleStreamTest.java | 11 +- ...ameWhenReadingBodyForSingleStreamTest.java | 11 +- ...verSendRequestAfterGoAwayScenarioTest.java | 10 +- .../Http2TcpServerSuccessScenarioTest.java | 11 +- native/src/test/resources/testng.xml | 1 + 10 files changed, 100 insertions(+), 160 deletions(-) delete mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index 010671f13e..dd4278489e 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -29,6 +29,7 @@ import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -62,6 +63,7 @@ public class Http2TcpServerGoAwayMultipleStreamScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayMultipleStreamScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -116,7 +118,6 @@ private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -126,8 +127,6 @@ private void runTcpServer(int port) { sendGoAwayForASingleStreamInAMultipleStreamScenario(outputStream); } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -158,4 +157,10 @@ private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputSt outputStream.write(DATA_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } + + @AfterClass + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java index 61e1f58950..6784d0be71 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -30,6 +30,7 @@ import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -58,19 +59,16 @@ public class Http2TcpServerGoAwaySingleStreamScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwaySingleStreamScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; @BeforeMethod public void setup(Method method) throws InterruptedException { - if (method.getName().equals("testGoAwayWhenReceivingHeadersForASingleStream")) { - runTcpServer(TestUtil.HTTP_SERVER_PORT, 1); - } else { - runTcpServer(TestUtil.HTTP_SERVER_PORT, 2); - } h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); } @Test (description = "In this, server sends headers and data for the accepted stream") private void testGoAwayWhenReceivingHeadersForASingleStream() { + runTcpServer(TestUtil.HTTP_SERVER_PORT, 1); HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { CountDownLatch latch = new CountDownLatch(1); @@ -88,6 +86,7 @@ private void testGoAwayWhenReceivingHeadersForASingleStream() { @Test (description = "In this, server exits without sending the headers and data for the accepted stream as well") private void testGoAwayAndServerExitWhenReceivingHeadersForASingleStream() { + runTcpServer(TestUtil.HTTP_SERVER_PORT, 2); HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { CountDownLatch latch = new CountDownLatch(1); @@ -110,7 +109,6 @@ private void testGoAwayAndServerExitWhenReceivingHeadersForASingleStream() { private void runTcpServer(int port, int option) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -124,8 +122,6 @@ private void runTcpServer(int port, int option) { } } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -158,4 +154,10 @@ private static void sendGoAwayForASingleStream(OutputStream outputStream) throws outputStream.write(DATA_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index e6e62bee99..76bd81ca76 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -29,6 +29,7 @@ import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -56,6 +57,7 @@ public class Http2TcpServerGoAwayWhileReceivingBodyScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -87,7 +89,6 @@ private void testGoAwayWhenReceivingBodyForASingleStream() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -97,8 +98,6 @@ private void runTcpServer(int port) { sendGoAwayAfterSendingHeadersForASingleStream(outputStream); } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -118,4 +117,10 @@ private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream o outputStream.write(GO_AWAY_FRAME_STREAM_01); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java deleted file mode 100644 index 0a8a5fa7c6..0000000000 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileSendingBodyScenarioTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.stdlib.http.transport.http2.frameleveltests; - -import io.ballerina.stdlib.http.transport.contract.Constants; -import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; -import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; -import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_01; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - -/** - * This contains a test case where the tcp server sends a GoAway while client sends the body. - */ -public class Http2TcpServerGoAwayWhileSendingBodyScenarioTest { - - private static final Logger LOGGER = - LoggerFactory.getLogger(Http2TcpServerGoAwayWhileSendingBodyScenarioTest.class); - - private HttpClientConnector h2ClientWithPriorKnowledge; - - @BeforeClass - public void setup() throws InterruptedException { - runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); - } - - @Test - private void testGoAwayWhenSendingBodyForASingleStream() { - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "hello world"); - try { - CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); - latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); - if (content != null) { - assertEquals(content.decoderResult().cause().getMessage(), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); - } else { - fail("Expected http content"); - } - } catch (InterruptedException e) { - LOGGER.error("Interrupted exception occurred"); - } - } - - private void runTcpServer(int port) { - new Thread(() -> { - ServerSocket serverSocket; - try { - serverSocket = new ServerSocket(port); - LOGGER.info("HTTP/2 TCP Server listening on port " + port); - Socket clientSocket = serverSocket.accept(); - LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); - try (OutputStream outputStream = clientSocket.getOutputStream()) { - sendGoAwayWhileSendingBodyForASingleStream(outputStream); - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); - } - } catch (IOException e) { - LOGGER.error(e.getMessage()); - } - }).start(); - } - - private static void sendGoAwayWhileSendingBodyForASingleStream(OutputStream outputStream) - throws IOException, InterruptedException { - // Sending settings frame with HEADER_TABLE_SIZE=25700 - outputStream.write(SETTINGS_FRAME); - Thread.sleep(SLEEP_TIME); - outputStream.write(SETTINGS_FRAME_WITH_ACK); - Thread.sleep(SLEEP_TIME); -// outputStream.write(HEADER_FRAME_STREAM_03); -// Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_01); - Thread.sleep(END_SLEEP_TIME); - } -} diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index 3ee33f07ad..cdcffdee21 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -30,6 +30,7 @@ import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -38,7 +39,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.Semaphore; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; @@ -60,6 +61,9 @@ public class Http2TcpServerRSTStreamFrameForMultipleStreamsTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameForMultipleStreamsTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + Semaphore readSemaphore = new Semaphore(0); + Semaphore writeSemaphore = new Semaphore(0); + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -68,22 +72,26 @@ public void setup() throws InterruptedException { } @Test - private void testRSTStreamFrameForSingleStream() { + private void testRSTStreamFrameForMultipleStreams() { HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { - CountDownLatch latch = new CountDownLatch(3); - DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch); + DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(new CountDownLatch(1)); HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); responseFuture1.setHttpConnectorListener(msgListener1); - DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch); + writeSemaphore.release(); + readSemaphore.acquire(); + DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(new CountDownLatch(1)); HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); responseFuture2.setHttpConnectorListener(msgListener2); - DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(latch); + writeSemaphore.release(); + readSemaphore.acquire(); + DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(new CountDownLatch(1)); HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); responseFuture3.setHttpConnectorListener(msgListener3); - latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + writeSemaphore.release(); + readSemaphore.acquire(); responseFuture1.sync(); responseFuture2.sync(); responseFuture3.sync(); @@ -92,7 +100,13 @@ private void testRSTStreamFrameForSingleStream() { assertEquals(throwable.getMessage(), Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } else { - fail("Expected an error"); +// HttpContent content1 = msgListener1.getHttpResponseMessage().getHttpContent(); +// if (content1 != null) { +// assertEquals(content1.decoderResult().cause().getMessage(), +// Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); +// } else { + fail("Expected an error"); +// } } HttpContent content2 = msgListener2.getHttpResponseMessage().getHttpContent(); assertEquals(content2.content().toString(CharsetUtil.UTF_8), "hello world5"); @@ -110,7 +124,6 @@ private void testRSTStreamFrameForSingleStream() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -120,29 +133,43 @@ private void runTcpServer(int port) { sendRSTStream(outputStream); } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); + } finally { + readSemaphore.release(); + writeSemaphore.release(); } }).start(); } // This will send an RST_STREAM frame for stream 3 before sending headers and a successful response for // stream 5 and an RST_STREAM frame for stream 7 after sending headers - private static void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { + private void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { + Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME_WITH_ACK); Thread.sleep(SLEEP_TIME); + writeSemaphore.acquire(); outputStream.write(RST_STREAM_FRAME_STREAM_03); + readSemaphore.release(); Thread.sleep(SLEEP_TIME); + writeSemaphore.acquire(); outputStream.write(HEADER_FRAME_STREAM_05); - outputStream.write(HEADER_FRAME_STREAM_07); - Thread.sleep(100); outputStream.write(DATA_FRAME_STREAM_05); + Thread.sleep(SLEEP_TIME); + readSemaphore.release(); + writeSemaphore.acquire(); + outputStream.write(HEADER_FRAME_STREAM_07); outputStream.write(RST_STREAM_FRAME_STREAM_07); + readSemaphore.release(); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index 49188b2768..7ebead82ea 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -28,6 +28,7 @@ import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -53,6 +54,7 @@ public class Http2TcpServerRSTStreamFrameForSingleStreamTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameForSingleStreamTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -84,7 +86,6 @@ private void testRSTStreamFrameForSingleStream() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -94,8 +95,6 @@ private void runTcpServer(int port) { sendRSTStream(outputStream); } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -112,4 +111,10 @@ private static void sendRSTStream(OutputStream outputStream) throws IOException, outputStream.write(RST_STREAM_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index 09b643dbb0..edf850fd15 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -29,6 +29,7 @@ import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -56,6 +57,7 @@ public class Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -87,7 +89,6 @@ private void testRSTStreamFrameWhenReadingBodyForSingleStream() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -97,8 +98,6 @@ private void runTcpServer(int port) { sendRSTStream(outputStream); } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -117,4 +116,10 @@ private static void sendRSTStream(OutputStream outputStream) throws IOException, outputStream.write(RST_STREAM_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 19b7f54c55..75405ec789 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -29,6 +29,7 @@ import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -61,6 +62,7 @@ public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { private HttpClientConnector h2ClientWithPriorKnowledge; private AtomicInteger numberOfConnections = new AtomicInteger(0); + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -101,7 +103,6 @@ private void testNewRequestAfterGoAwayReceivedScenario() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -122,7 +123,6 @@ private void runTcpServer(int port) { LOGGER.error(e.getMessage()); } } - serverSocket.close(); } catch (IOException e) { LOGGER.error(e.getMessage()); } @@ -150,4 +150,10 @@ private static void sendSuccessfulResponse(OutputStream outputStream) throws IOE outputStream.write(DATA_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java index a0dc863474..f39e3e9048 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java @@ -29,6 +29,7 @@ import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -54,6 +55,7 @@ public class Http2TcpServerSuccessScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2TcpServerSuccessScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; @BeforeClass public void setup() throws InterruptedException { @@ -80,7 +82,6 @@ private void testSuccessfulConnection() { private void runTcpServer(int port) { new Thread(() -> { - ServerSocket serverSocket; try { serverSocket = new ServerSocket(port); LOGGER.info("HTTP/2 TCP Server listening on port " + port); @@ -90,8 +91,6 @@ private void runTcpServer(int port) { sendSuccessfulResponse(outputStream); } catch (Exception e) { LOGGER.error(e.getMessage()); - } finally { - serverSocket.close(); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -110,4 +109,10 @@ private static void sendSuccessfulResponse(OutputStream outputStream) throws IOE outputStream.write(DATA_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } + + @AfterMethod + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } } diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index 2f73f51d38..ca73553934 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -161,6 +161,7 @@ + From be287b590e1bb7c38528ba63b433ac1c3fb26402 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 9 Jan 2024 12:24:17 +0530 Subject: [PATCH 12/46] Update test cases --- ...Http2TcpServerGoAwayMultipleStreamScenarioTest.java | 4 ++-- .../Http2TcpServerGoAwaySingleStreamScenarioTest.java | 10 +++++----- ...2TcpServerGoAwayWhileReceivingBodyScenarioTest.java | 6 +++--- ...2TcpServerRSTStreamFrameForMultipleStreamsTest.java | 8 +------- ...ttp2TcpServerRSTStreamFrameForSingleStreamTest.java | 2 +- ...TStreamFrameWhenReadingBodyForSingleStreamTest.java | 2 +- ...tp2TcpServerSendRequestAfterGoAwayScenarioTest.java | 8 ++++---- .../Http2TcpServerSuccessScenarioTest.java | 2 +- .../transport/http2/frameleveltests/TestUtils.java | 8 ++++++-- 9 files changed, 24 insertions(+), 26 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index dd4278489e..e51c89cbe0 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -134,7 +134,7 @@ private void runTcpServer(int port) { }).start(); } - private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputStream outputStream) + private void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); @@ -161,6 +161,6 @@ private static void sendGoAwayForASingleStreamInAMultipleStreamScenario(OutputSt @AfterClass public void cleanUp() throws IOException { h2ClientWithPriorKnowledge.close(); - serverSocket.close(); + serverSocket.close(); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java index 6784d0be71..b6d2a72657 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -44,7 +44,7 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; @@ -129,25 +129,25 @@ private void runTcpServer(int port, int option) { }).start(); } - private static void sendGoAwayAndExitForASingleStream(OutputStream outputStream) + private void sendGoAwayAndExitForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME_WITH_ACK); Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_03); + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_03); // Once the sleep time elapses, channel inactive of client gets fired. Thread.sleep(SLEEP_TIME); } - private static void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + private void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME_WITH_ACK); Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_03); + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_03); Thread.sleep(SLEEP_TIME); outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index 76bd81ca76..5d56ab140d 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -41,7 +41,7 @@ import java.util.concurrent.TimeUnit; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_01; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_01; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; @@ -105,7 +105,7 @@ private void runTcpServer(int port) { }).start(); } - private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream outputStream) + private void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); @@ -114,7 +114,7 @@ private static void sendGoAwayAfterSendingHeadersForASingleStream(OutputStream o Thread.sleep(SLEEP_TIME); outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_01); + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_01); Thread.sleep(END_SLEEP_TIME); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index cdcffdee21..f6e8af0780 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -100,13 +100,7 @@ private void testRSTStreamFrameForMultipleStreams() { assertEquals(throwable.getMessage(), Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } else { -// HttpContent content1 = msgListener1.getHttpResponseMessage().getHttpContent(); -// if (content1 != null) { -// assertEquals(content1.decoderResult().cause().getMessage(), -// Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); -// } else { - fail("Expected an error"); -// } + fail("Expected an error"); } HttpContent content2 = msgListener2.getHttpResponseMessage().getHttpContent(); assertEquals(content2.content().toString(CharsetUtil.UTF_8), "hello world5"); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index 7ebead82ea..c9bce5139b 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -102,7 +102,7 @@ private void runTcpServer(int port) { }).start(); } - private static void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { + private void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index edf850fd15..2d803c0d89 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -105,7 +105,7 @@ private void runTcpServer(int port) { }).start(); } - private static void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { + private void sendRSTStream(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 75405ec789..7fe332c2c9 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -43,7 +43,7 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; @@ -129,17 +129,17 @@ private void runTcpServer(int port) { }).start(); } - private static void sendGoAway(OutputStream outputStream) throws IOException, InterruptedException { + private void sendGoAway(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); - outputStream.write(GO_AWAY_FRAME_STREAM_03); + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_03); Thread.sleep(END_SLEEP_TIME); } - private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { + private void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java index f39e3e9048..e6dd0cdf0b 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java @@ -98,7 +98,7 @@ private void runTcpServer(int port) { }).start(); } - private static void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { + private void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java index a30a3d11c0..9fd52fb6cd 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java @@ -54,12 +54,16 @@ public class TestUtils { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x37}; public static final byte[] DATA_FRAME_STREAM_09 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x39}; - public static final byte[] GO_AWAY_FRAME_STREAM_01 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + public static final byte[] GO_AWAY_FRAME_MAX_STREAM_01 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b}; - public static final byte[] GO_AWAY_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + public static final byte[] GO_AWAY_FRAME_MAX_STREAM_03 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b}; + public static final byte[] GO_AWAY_FRAME_MAX_STREAM_05 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0b}; public static final byte[] GO_AWAY_FRAME_MAX_STREAM_07 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0b}; + public static final byte[] GO_AWAY_FRAME_MAX_STREAM_09 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b}; public static final byte[] RST_STREAM_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02}; public static final byte[] RST_STREAM_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, From 6dac82d0673efbe72afd0ffaaa23b40d0a92fadf Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 9 Jan 2024 17:00:23 +0530 Subject: [PATCH 13/46] Update goaway test case --- ...verSendRequestAfterGoAwayScenarioTest.java | 19 +++++++++---------- .../http2/frameleveltests/TestUtils.java | 2 ++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 7fe332c2c9..9097f6fd8b 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -42,6 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; @@ -87,15 +88,10 @@ private void testNewRequestAfterGoAwayReceivedScenario() { responseFuture2.setHttpConnectorListener(msgListener2); latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); responseFuture2.sync(); - Throwable responseError = responseFuture1.getStatus().getCause(); - if (responseError != null) { - assertEquals(responseError.getMessage(), - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); - } else { - fail("Expected error not received"); - } + HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); + assertEquals(response1.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); - assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); + assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } @@ -133,9 +129,12 @@ private void sendGoAway(OutputStream outputStream) throws IOException, Interrupt // Sending settings frame with HEADER_TABLE_SIZE=25700 outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); - outputStream.write(SETTINGS_FRAME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); Thread.sleep(SLEEP_TIME); outputStream.write(GO_AWAY_FRAME_MAX_STREAM_03); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_03); Thread.sleep(END_SLEEP_TIME); } @@ -147,7 +146,7 @@ private void sendSuccessfulResponse(OutputStream outputStream) throws IOExceptio Thread.sleep(SLEEP_TIME); outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); - outputStream.write(DATA_FRAME_STREAM_03); + outputStream.write(DATA_FRAME_STREAM_03_DIFFERENT_DATA); Thread.sleep(END_SLEEP_TIME); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java index 9fd52fb6cd..57881edf8d 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java @@ -48,6 +48,8 @@ public class TestUtils { (byte) 0x88, 0x5f, (byte) 0x87, 0x49, 0x7c, (byte) 0xa5, (byte) 0x8a, (byte) 0xe8, 0x19, (byte) 0xaa}; public static final byte[] DATA_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x33}; + public static final byte[] DATA_FRAME_STREAM_03_DIFFERENT_DATA = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x35}; public static final byte[] DATA_FRAME_STREAM_05 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x35}; public static final byte[] DATA_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, From 1014fd255e5aeef98496a6308a6f6c1354d963ed Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 9 Jan 2024 17:00:43 +0530 Subject: [PATCH 14/46] Check exhausted before returning to pool --- .../transport/contractimpl/sender/http2/Http2ClientChannel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 218efc1084..6f29d8299e 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -290,7 +290,7 @@ public void onStreamClosed(Http2Stream stream) { activeStreams.decrementAndGet(); http2ClientChannel.getDataEventListeners(). forEach(dataEventListener -> dataEventListener.onStreamClose(stream.id())); - if (isExhausted.getAndSet(false)) { + if (!isExhausted.get() && isExhausted.getAndSet(false)) { http2ConnectionManager.returnClientChannel(httpRoute, http2ClientChannel); } } From 1e525136b565946aee82a9a32532a2cf99dca4b3 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 9 Jan 2024 17:03:44 +0530 Subject: [PATCH 15/46] Remove unused imports --- .../Http2TcpServerSendRequestAfterGoAwayScenarioTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 9097f6fd8b..0a91869c1f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -18,7 +18,6 @@ package io.ballerina.stdlib.http.transport.http2.frameleveltests; -import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; @@ -50,7 +49,6 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; /** * This contains a test case where the client sends a request after receiving a GoAway. From e5ad5d87b277385b75bfcf1c8da6d7590e87c791 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 10 Jan 2024 23:03:56 +0530 Subject: [PATCH 16/46] Add a locking mechanism for conn manager --- .../contractimpl/sender/http2/Http2ClientChannel.java | 6 ++++-- .../sender/http2/Http2ConnectionManager.java | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 6f29d8299e..ba1758aa12 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -254,7 +254,7 @@ void destroy() { this.connection.removeListener(streamCloseListener); inFlightMessages.clear(); promisedMessages.clear(); - http2ConnectionManager.removeClientChannel(httpRoute, this); + removeFromConnectionPool(); } /** @@ -297,8 +297,10 @@ public void onStreamClosed(Http2Stream stream) { @Override public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - isExhausted.set(true); + markAsExhausted(); + http2ConnectionManager.getLock().lock(); removeFromConnectionPool(); + http2ConnectionManager.getLock().unlock(); http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { if (streamId > lastStreamId) { http2ClientChannel.removeInFlightMessage(streamId); diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index 18feaf8c90..a05561a917 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -21,6 +21,9 @@ import io.ballerina.stdlib.http.transport.contractimpl.common.HttpRoute; import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + /** * {@code Http2ConnectionManager} Manages HTTP/2 connections. */ @@ -28,6 +31,7 @@ public class Http2ConnectionManager { private final Http2ChannelPool http2ChannelPool = new Http2ChannelPool(); private final PoolConfiguration poolConfiguration; + private Lock lock = new ReentrantLock(); public Http2ConnectionManager(PoolConfiguration poolConfiguration) { this.poolConfiguration = poolConfiguration; @@ -112,12 +116,14 @@ private synchronized Http2ChannelPool.PerRouteConnectionPool createPerRouteConne */ public Http2ClientChannel borrowChannel(HttpRoute httpRoute) { Http2ChannelPool.PerRouteConnectionPool perRouteConnectionPool; + lock.lock(); perRouteConnectionPool = getOrCreatePerRoutePool(this.http2ChannelPool, generateKey(httpRoute)); Http2ClientChannel http2ClientChannel = null; if (perRouteConnectionPool != null) { http2ClientChannel = perRouteConnectionPool.fetchTargetChannel(); } + lock.unlock(); return http2ClientChannel; } @@ -156,4 +162,8 @@ private String generateKey(HttpRoute httpRoute) { return httpRoute.getScheme() + ":" + httpRoute.getHost() + ":" + httpRoute.getPort() + ":" + httpRoute.getConfigHash(); } + + public Lock getLock() { + return lock; + } } From 21a05d5f3ec635e1c748841c99338626630be979 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 10 Jan 2024 23:04:07 +0530 Subject: [PATCH 17/46] Update test cases --- ...verSendRequestAfterGoAwayScenarioTest.java | 73 +++++++++++++------ .../http2/frameleveltests/TestUtils.java | 2 +- .../util/DefaultHttpConnectorListener.java | 11 ++- 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java index 0a91869c1f..c894eae5a5 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java @@ -36,9 +36,7 @@ import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Semaphore; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; @@ -60,8 +58,8 @@ public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { LoggerFactory.getLogger(Http2TcpServerSendRequestAfterGoAwayScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; - private AtomicInteger numberOfConnections = new AtomicInteger(0); private ServerSocket serverSocket; + Semaphore semaphore = new Semaphore(0); @BeforeClass public void setup() throws InterruptedException { @@ -70,26 +68,56 @@ public void setup() throws InterruptedException { } @Test - private void testNewRequestAfterGoAwayReceivedScenario() { + private void testGoAwayForAllStreamsScenario() { HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage4 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage5 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage6 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { - CountDownLatch latch1 = new CountDownLatch(1); - CountDownLatch latch2 = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch1); + DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(); HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); responseFuture1.setHttpConnectorListener(msgListener1); - latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + semaphore.acquire(); responseFuture1.sync(); - DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch2); + DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(); HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); responseFuture2.setHttpConnectorListener(msgListener2); - latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + semaphore.acquire(); responseFuture2.sync(); + DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(); + HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); + responseFuture3.setHttpConnectorListener(msgListener3); + semaphore.acquire(); + responseFuture3.sync(); + DefaultHttpConnectorListener msgListener4 = new DefaultHttpConnectorListener(); + HttpResponseFuture responseFuture4 = h2ClientWithPriorKnowledge.send(httpCarbonMessage4); + responseFuture4.setHttpConnectorListener(msgListener4); + semaphore.acquire(); + responseFuture4.sync(); + DefaultHttpConnectorListener msgListener5 = new DefaultHttpConnectorListener(); + HttpResponseFuture responseFuture5 = h2ClientWithPriorKnowledge.send(httpCarbonMessage5); + responseFuture5.setHttpConnectorListener(msgListener5); + semaphore.acquire(); + responseFuture5.sync(); + DefaultHttpConnectorListener msgListener6 = new DefaultHttpConnectorListener(); + HttpResponseFuture responseFuture6 = h2ClientWithPriorKnowledge.send(httpCarbonMessage6); + responseFuture6.setHttpConnectorListener(msgListener6); + semaphore.acquire(); + responseFuture6.sync(); HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); assertEquals(response1.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); + HttpCarbonMessage response3 = msgListener3.getHttpResponseMessage(); + assertEquals(response3.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); + HttpCarbonMessage response4 = msgListener4.getHttpResponseMessage(); + assertEquals(response4.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); + HttpCarbonMessage response5 = msgListener5.getHttpResponseMessage(); + assertEquals(response5.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); + HttpCarbonMessage response6 = msgListener6.getHttpResponseMessage(); + assertEquals(response6.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } @@ -99,20 +127,18 @@ private void runTcpServer(int port) { new Thread(() -> { try { serverSocket = new ServerSocket(port); + int numberOfConnections = 0; LOGGER.info("HTTP/2 TCP Server listening on port " + port); - while (true) { + while (numberOfConnections < 6) { Socket clientSocket = serverSocket.accept(); LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); try (OutputStream outputStream = clientSocket.getOutputStream()) { - if (numberOfConnections.get() == 0) { - sendGoAway(outputStream); - numberOfConnections.set(1); - } else if (numberOfConnections.get() == 1) { - sendSuccessfulResponse(outputStream); - break; + if (numberOfConnections % 2 == 0) { + sendGoAwayBeforeSendingHeaders(outputStream); } else { - break; + sendGoAwayAfterSendingHeaders(outputStream); } + numberOfConnections += 1; } catch (Exception e) { LOGGER.error(e.getMessage()); } @@ -123,8 +149,7 @@ private void runTcpServer(int port) { }).start(); } - private void sendGoAway(OutputStream outputStream) throws IOException, InterruptedException { - // Sending settings frame with HEADER_TABLE_SIZE=25700 + private void sendGoAwayBeforeSendingHeaders(OutputStream outputStream) throws IOException, InterruptedException { outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME_WITH_ACK); @@ -133,18 +158,20 @@ private void sendGoAway(OutputStream outputStream) throws IOException, Interrupt outputStream.write(HEADER_FRAME_STREAM_03); Thread.sleep(SLEEP_TIME); outputStream.write(DATA_FRAME_STREAM_03); + semaphore.release(); Thread.sleep(END_SLEEP_TIME); } - private void sendSuccessfulResponse(OutputStream outputStream) throws IOException, InterruptedException { - // Sending settings frame with HEADER_TABLE_SIZE=25700 + private void sendGoAwayAfterSendingHeaders(OutputStream outputStream) throws IOException, InterruptedException { outputStream.write(SETTINGS_FRAME); Thread.sleep(SLEEP_TIME); outputStream.write(SETTINGS_FRAME_WITH_ACK); Thread.sleep(SLEEP_TIME); outputStream.write(HEADER_FRAME_STREAM_03); + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_03); Thread.sleep(SLEEP_TIME); outputStream.write(DATA_FRAME_STREAM_03_DIFFERENT_DATA); + semaphore.release(); Thread.sleep(END_SLEEP_TIME); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java index 57881edf8d..2d8674347d 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java @@ -32,7 +32,7 @@ public class TestUtils { public static final int SLEEP_TIME = 100; - public static final int END_SLEEP_TIME = 5000; + public static final int END_SLEEP_TIME = 1000; public static final byte[] SETTINGS_FRAME = new byte[]{0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x64}; diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/util/DefaultHttpConnectorListener.java b/native/src/test/java/io/ballerina/stdlib/http/transport/util/DefaultHttpConnectorListener.java index 26034afde7..78a3958e83 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/util/DefaultHttpConnectorListener.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/util/DefaultHttpConnectorListener.java @@ -36,16 +36,23 @@ public DefaultHttpConnectorListener(CountDownLatch latch) { this.latch = latch; } + // This constructor can be used to create a listener without a latch when an external locking mechanism is in place. + public DefaultHttpConnectorListener() {} + @Override public void onMessage(HttpCarbonMessage httpMessage) { this.httpMessage = httpMessage; - latch.countDown(); + if (latch != null) { + latch.countDown(); + } } @Override public void onError(Throwable throwable) { this.throwable = throwable; - latch.countDown(); + if (latch != null) { + latch.countDown(); + } } public HttpCarbonMessage getHttpResponseMessage() { From a72df1bd6d36d2d8c596b0fd850f593e5b224739 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 10 Jan 2024 23:42:00 +0530 Subject: [PATCH 18/46] Fix spotbug --- .../sender/http2/Http2ConnectionManager.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index a05561a917..e0736606ac 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -116,15 +116,18 @@ private synchronized Http2ChannelPool.PerRouteConnectionPool createPerRouteConne */ public Http2ClientChannel borrowChannel(HttpRoute httpRoute) { Http2ChannelPool.PerRouteConnectionPool perRouteConnectionPool; - lock.lock(); - perRouteConnectionPool = getOrCreatePerRoutePool(this.http2ChannelPool, generateKey(httpRoute)); - - Http2ClientChannel http2ClientChannel = null; - if (perRouteConnectionPool != null) { - http2ClientChannel = perRouteConnectionPool.fetchTargetChannel(); + try { + getLock().lock(); + perRouteConnectionPool = getOrCreatePerRoutePool(this.http2ChannelPool, generateKey(httpRoute)); + + Http2ClientChannel http2ClientChannel = null; + if (perRouteConnectionPool != null) { + http2ClientChannel = perRouteConnectionPool.fetchTargetChannel(); + } + return http2ClientChannel; + } finally { + getLock().unlock(); } - lock.unlock(); - return http2ClientChannel; } /** From 5989f149061db4e50a7ba004eb3f8a3e65ceea80 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Thu, 11 Jan 2024 08:45:49 +0530 Subject: [PATCH 19/46] Update test name --- ...=> Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java} | 4 ++-- native/src/test/resources/testng.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/{Http2TcpServerSendRequestAfterGoAwayScenarioTest.java => Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java} (98%) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java similarity index 98% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java index c894eae5a5..6c92aab9c4 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendRequestAfterGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java @@ -52,10 +52,10 @@ * This contains a test case where the client sends a request after receiving a GoAway. * This tests whether there is a new connection opened from the client. */ -public class Http2TcpServerSendRequestAfterGoAwayScenarioTest { +public class Http2TcpServerSendGoAwayForAllStreamsScenarioTest { private static final Logger LOGGER = - LoggerFactory.getLogger(Http2TcpServerSendRequestAfterGoAwayScenarioTest.class); + LoggerFactory.getLogger(Http2TcpServerSendGoAwayForAllStreamsScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; private ServerSocket serverSocket; diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index ca73553934..3fa12c9b01 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -157,7 +157,7 @@ - + From c0d266c5aec0974eac3849cecb53e3e59ab399f7 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 16 Jan 2024 16:20:42 +0530 Subject: [PATCH 20/46] Add connection timeout logic --- ballerina/http_client_connection_pool.bal | 3 ++ .../stdlib/http/api/HttpConstants.java | 2 + .../ballerina/stdlib/http/api/HttpUtil.java | 3 ++ .../DefaultHttpClientConnector.java | 2 +- .../channel/pool/PoolConfiguration.java | 9 +++++ .../sender/http2/Http2ClientChannel.java | 19 ++++++++- .../sender/http2/Http2ConnectionManager.java | 40 ++++++++++++++++++- .../http2/TestExhaustedStreamIdForClient.java | 2 +- 8 files changed, 76 insertions(+), 4 deletions(-) diff --git a/ballerina/http_client_connection_pool.bal b/ballerina/http_client_connection_pool.bal index c2598bca7f..0468fb1549 100644 --- a/ballerina/http_client_connection_pool.bal +++ b/ballerina/http_client_connection_pool.bal @@ -27,12 +27,15 @@ configurable int maxActiveStreamsPerConnection = 100; # + maxIdleConnections - Maximum number of idle connections allowed per pool. # + waitTime - Maximum amount of time (in seconds), the client should wait for an idle connection before it sends an error when the pool is exhausted # + maxActiveStreamsPerConnection - Maximum active streams per connection. This only applies to HTTP/2. Default value is 100 +# + http2ConnectionIdleTimeout - HTTP2 connection idle timeout in seconds. This only applies to HTTP/2. Default value is 30 seconds public type PoolConfiguration record {| int maxActiveConnections = maxActiveConnections; int maxIdleConnections = maxIdleConnections; decimal waitTime = waitTime; int maxActiveStreamsPerConnection = maxActiveStreamsPerConnection; + int http2ConnectionIdleTimeout = 30; |}; + //This is a hack to get the global map initialized, without involving locking. class ConnectionManager { public PoolConfiguration & readonly poolConfig = {}; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java index dc5f4fc2f6..4b748122c4 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java @@ -506,6 +506,8 @@ public final class HttpConstants { public static final BString CONNECTION_POOLING_WAIT_TIME = StringUtils.fromString("waitTime"); public static final BString CONNECTION_POOLING_MAX_ACTIVE_STREAMS_PER_CONNECTION = StringUtils.fromString( "maxActiveStreamsPerConnection"); + public static final BString CONNECTION_POOLING_HTTP2_CONNECTION_IDLE_TIMEOUT = StringUtils.fromString( + "http2ConnectionIdleTimeout"); public static final String HTTP_CLIENT_CONNECTION_POOL = "PoolConfiguration"; public static final String CONNECTION_MANAGER = "ConnectionManager"; public static final int POOL_CONFIG_INDEX = 1; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java index e470d0ab71..e8ab99685e 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java @@ -133,6 +133,7 @@ import static io.ballerina.runtime.observability.ObservabilityConstants.TAG_KEY_PEER_ADDRESS; import static io.ballerina.stdlib.http.api.HttpConstants.ANN_CONFIG_ATTR_COMPRESSION_CONTENT_TYPES; import static io.ballerina.stdlib.http.api.HttpConstants.ANN_CONFIG_ATTR_SSL_ENABLED_PROTOCOLS; +import static io.ballerina.stdlib.http.api.HttpConstants.CONNECTION_POOLING_HTTP2_CONNECTION_IDLE_TIMEOUT; import static io.ballerina.stdlib.http.api.HttpConstants.CREATE_INTERCEPTORS_FUNCTION_NAME; import static io.ballerina.stdlib.http.api.HttpConstants.ENDPOINT_CONFIG_HTTP2_INITIAL_WINDOW_SIZE; import static io.ballerina.stdlib.http.api.HttpConstants.HTTP_HEADERS; @@ -1352,6 +1353,8 @@ public static void populatePoolingConfig(BMap poolRecord, PoolConfiguration pool maxActiveStreamsPerConnection == -1 ? Integer.MAX_VALUE : validateConfig( maxActiveStreamsPerConnection, HttpConstants.CONNECTION_POOLING_MAX_ACTIVE_STREAMS_PER_CONNECTION.getValue())); + long http2ConnectionIdleTimeout = poolRecord.getIntValue(CONNECTION_POOLING_HTTP2_CONNECTION_IDLE_TIMEOUT); + poolConfiguration.setHttp2ConnectionIdleTimeout(http2ConnectionIdleTimeout * 1000); } private static int validateConfig(long value, String configName) { diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/DefaultHttpClientConnector.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/DefaultHttpClientConnector.java index 53da6e9c9b..1b6a34b4fc 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/DefaultHttpClientConnector.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/DefaultHttpClientConnector.java @@ -183,7 +183,7 @@ public HttpResponseFuture send(OutboundMsgHolder outboundMsgHolder, HttpCarbonMe this.configHashCode); if (http2) { // See whether an already upgraded HTTP/2 connection is available - Http2ClientChannel activeHttp2ClientChannel = http2ConnectionManager.borrowChannel(route); + Http2ClientChannel activeHttp2ClientChannel = http2ConnectionManager.fetchChannel(route); if (activeHttp2ClientChannel != null) { outboundMsgHolder.setHttp2ClientChannel(activeHttp2ClientChannel); diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java index 9ab58dc6d0..d14c47fb67 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java @@ -35,6 +35,7 @@ public class PoolConfiguration { private int eventGroupExecutorThreads = 15; private long maxWaitTime = 60000L; private int http2MaxActiveStreamsPerConnection = Integer.MAX_VALUE; + private long http2ConnectionIdleTimeout = 30000; public PoolConfiguration() { } @@ -142,4 +143,12 @@ public int getHttp2MaxActiveStreamsPerConnection() { public void setHttp2MaxActiveStreamsPerConnection(int http2MaxActiveStreamsPerConnection) { this.http2MaxActiveStreamsPerConnection = http2MaxActiveStreamsPerConnection; } + + public long getHttp2ConnectionIdleTimeout() { + return http2ConnectionIdleTimeout; + } + + public void setHttp2ConnectionIdleTimeout(long http2ConnectionIdleTimeout) { + this.http2ConnectionIdleTimeout = http2ConnectionIdleTimeout; + } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index ba1758aa12..d6213e6d31 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -61,6 +61,7 @@ public class Http2ClientChannel { private int socketIdleTimeout = Constants.ENDPOINT_TIMEOUT; private Map dataEventListeners; private StreamCloseListener streamCloseListener; + private long timeSinceMarkedAsStale = 0; public Http2ClientChannel(Http2ConnectionManager http2ConnectionManager, Http2Connection connection, HttpRoute httpRoute, Channel channel) { @@ -299,7 +300,7 @@ public void onStreamClosed(Http2Stream stream) { public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { markAsExhausted(); http2ConnectionManager.getLock().lock(); - removeFromConnectionPool(); + markAsStale(); http2ConnectionManager.getLock().unlock(); http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { if (streamId > lastStreamId) { @@ -320,4 +321,20 @@ public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData void removeFromConnectionPool() { http2ConnectionManager.removeClientChannel(httpRoute, this); } + + void markAsStale() { + http2ConnectionManager.markClientChannelAsStale(httpRoute, this); + } + + boolean hasInFlightMessages() { + return !inFlightMessages.isEmpty(); + } + + public void setTimeSinceMarkedAsStale(long timeSinceMarkedAsStale) { + this.timeSinceMarkedAsStale = timeSinceMarkedAsStale; + } + + public long getTimeSinceMarkedAsStale() { + return timeSinceMarkedAsStale; + } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index e0736606ac..0be44f9a07 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -20,7 +20,12 @@ import io.ballerina.stdlib.http.transport.contractimpl.common.HttpRoute; import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; +import io.netty.channel.DefaultChannelPromise; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -30,11 +35,13 @@ public class Http2ConnectionManager { private final Http2ChannelPool http2ChannelPool = new Http2ChannelPool(); + private final BlockingQueue http2StaleClientChannels = new LinkedBlockingQueue<>(); private final PoolConfiguration poolConfiguration; private Lock lock = new ReentrantLock(); public Http2ConnectionManager(PoolConfiguration poolConfiguration) { this.poolConfiguration = poolConfiguration; + initiateConnectionTimeoutTask(); } /** @@ -114,7 +121,7 @@ private synchronized Http2ChannelPool.PerRouteConnectionPool createPerRouteConne * @param httpRoute the http route * @return Http2ClientChannel */ - public Http2ClientChannel borrowChannel(HttpRoute httpRoute) { + public Http2ClientChannel fetchChannel(HttpRoute httpRoute) { Http2ChannelPool.PerRouteConnectionPool perRouteConnectionPool; try { getLock().lock(); @@ -156,6 +163,37 @@ void removeClientChannel(HttpRoute httpRoute, Http2ClientChannel http2ClientChan } } + void markClientChannelAsStale(HttpRoute httpRoute, Http2ClientChannel http2ClientChannel) { + Http2ChannelPool.PerRouteConnectionPool perRouteConnectionPool = fetchPerRoutePool(httpRoute); + if (perRouteConnectionPool != null) { + perRouteConnectionPool.removeChannel(http2ClientChannel); + } + http2ClientChannel.setTimeSinceMarkedAsStale(System.currentTimeMillis()); + http2StaleClientChannels.add(http2ClientChannel); + } + + private void initiateConnectionTimeoutTask() { + Timer timer = new Timer(true); + TimerTask timerTask = new TimerTask() { + @Override + public void run() { + System.out.println("\nRunning timer task"); + http2StaleClientChannels.forEach(http2ClientChannel -> { + System.out.println("Found stale channel" + http2ClientChannel.hashCode()); + if ((System.currentTimeMillis() - http2ClientChannel.getTimeSinceMarkedAsStale()) > + poolConfiguration.getHttp2ConnectionIdleTimeout() + && !http2ClientChannel.hasInFlightMessages()) { + System.out.println("Closing stale channel" + http2ClientChannel.hashCode()); + http2StaleClientChannels.remove(http2ClientChannel); + http2ClientChannel.getConnection() + .close(new DefaultChannelPromise(http2ClientChannel.getChannel())); + } + }); + } + }; + timer.schedule(timerTask, 10000, 30000); + } + private Http2ChannelPool.PerRouteConnectionPool fetchPerRoutePool(HttpRoute httpRoute) { String key = generateKey(httpRoute); return this.http2ChannelPool.fetchPerRoutePool(key); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java index 46bff8fdc7..c46f1ee591 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java @@ -100,7 +100,7 @@ public void testExhaustedStreamId() throws Http2Exception { assertEquals(firstResult, testValue, "Expected response not received"); Http2ClientChannel http2ClientChannel = connectionManager.getHttp2ConnectionManager() - .borrowChannel(new HttpRoute(Constants.HTTP_SCHEME, LOCALHOST, + .fetchChannel(new HttpRoute(Constants.HTTP_SCHEME, LOCALHOST, HTTP_SERVER_PORT, 0)); //Simulate the stream id to have reached its max value for the connection. From 2b2e4271da9eba1a273efb29423244ae0a54793a Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 16 Jan 2024 20:06:59 +0530 Subject: [PATCH 21/46] Remove unnecessary print statements --- .../contractimpl/sender/http2/Http2ConnectionManager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index 0be44f9a07..694bfd9919 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -177,13 +177,10 @@ private void initiateConnectionTimeoutTask() { TimerTask timerTask = new TimerTask() { @Override public void run() { - System.out.println("\nRunning timer task"); http2StaleClientChannels.forEach(http2ClientChannel -> { - System.out.println("Found stale channel" + http2ClientChannel.hashCode()); if ((System.currentTimeMillis() - http2ClientChannel.getTimeSinceMarkedAsStale()) > poolConfiguration.getHttp2ConnectionIdleTimeout() && !http2ClientChannel.hasInFlightMessages()) { - System.out.println("Closing stale channel" + http2ClientChannel.hashCode()); http2StaleClientChannels.remove(http2ClientChannel); http2ClientChannel.getConnection() .close(new DefaultChannelPromise(http2ClientChannel.getChannel())); From ee3d31c301f38642ee86e1d3ffe6c7ef0c50ed76 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 11:43:50 +0530 Subject: [PATCH 22/46] Add test case for conn timeout --- ...meoutAfterTcpServerGoAwayScenarioTest.java | 194 ++++++++++++++++++ native/src/test/resources/testng.xml | 1 + 2 files changed, 195 insertions(+) create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java new file mode 100644 index 0000000000..70173a2924 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.stdlib.http.transport.http2.frameleveltests; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; +import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; +import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; +import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static org.testng.Assert.assertEqualsNoOrder; + +/** + * This contains a test case where the tcp server sends a GoAway and the connection gets timed out from client side. + */ +public class Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; + private int numOfConnections = 0; + + @BeforeClass + public void setup() throws InterruptedException { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = setupHttp2PriorKnowledgeClient(); + } + + public HttpClientConnector setupHttp2PriorKnowledgeClient() { + HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); + PoolConfiguration poolConfiguration = new PoolConfiguration(); + poolConfiguration.setHttp2ConnectionIdleTimeout(5000); + TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); + SenderConfiguration senderConfiguration = new SenderConfiguration(); + senderConfiguration.setPoolConfiguration(poolConfiguration); + senderConfiguration.setScheme(Constants.HTTP_SCHEME); + senderConfiguration.setHttpVersion(Constants.HTTP_2_0); + senderConfiguration.setForceHttp2(true); + return connectorFactory.createHttpClientConnector( + HttpConnectorUtil.getTransportProperties(transportsConfiguration), senderConfiguration); + } + + @Test + private void testConnectionTimeoutAfterServerGoAwayScenario() { + HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + try { + CountDownLatch latch1 = new CountDownLatch(2); + DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch1); + HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); + responseFuture1.setHttpConnectorListener(msgListener1); + + DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch1); + HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); + responseFuture2.setHttpConnectorListener(msgListener2); + + latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture1.sync(); + responseFuture2.sync(); + + CountDownLatch latch2 = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(latch2); + HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); + responseFuture3.setHttpConnectorListener(msgListener3); + + latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + responseFuture3.sync(); + + HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); + HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); + HttpCarbonMessage response3 = msgListener3.getHttpResponseMessage(); + + Object responseVal1 = response1.getHttpContent().content().toString(CharsetUtil.UTF_8); + Object responseVal2 = response2.getHttpContent().content().toString(CharsetUtil.UTF_8); + Object responseVal3 = response3.getHttpContent().content().toString(CharsetUtil.UTF_8); + assertEqualsNoOrder(List.of(responseVal1, responseVal2, responseVal3), + List.of("hello world3", "hello world5", "hello world5")); + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + } + } + + private void runTcpServer(int port) { + new Thread(() -> { + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + while (numOfConnections < 2) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream()) { + if (numOfConnections == 0) { + sendGoAwayForASingleStream(outputStream); + numOfConnections += 1; + } else { + // If the connection successfully closed, a new socket connection + // will be opened and it will come here + sendSuccessfulRequest(outputStream); + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + private void sendGoAwayForASingleStream(OutputStream outputStream) throws IOException, InterruptedException { + outputStream.write(SETTINGS_FRAME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + // This will move the connection to the stale connections list + outputStream.write(GO_AWAY_FRAME_MAX_STREAM_05); + // Sleeping for 8 seconds and the timer task will check whether there are inflight message still + // remaining in the channel. + Thread.sleep(8000); + outputStream.write(DATA_FRAME_STREAM_03); + outputStream.write(HEADER_FRAME_STREAM_05); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_05); + // Once all the inflight messages are completed, the connection will be closed. + Thread.sleep(8000); + } + + private void sendSuccessfulRequest(OutputStream outputStream) throws IOException, InterruptedException { + outputStream.write(SETTINGS_FRAME); + outputStream.write(SETTINGS_FRAME_WITH_ACK); + Thread.sleep(SLEEP_TIME); + outputStream.write(HEADER_FRAME_STREAM_03); + Thread.sleep(SLEEP_TIME); + outputStream.write(DATA_FRAME_STREAM_03_DIFFERENT_DATA); + Thread.sleep(END_SLEEP_TIME); + } + + @AfterClass + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } +} diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index 3fa12c9b01..51418dbce6 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -162,6 +162,7 @@ + From be0b2be95a22c51ec84c2e577c61f8f5114f7a19 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 16:27:19 +0530 Subject: [PATCH 23/46] Rename class --- .../contractimpl/sender/HttpClientChannelInitializer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java index 8b83a42147..6bf4c22d32 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java @@ -219,7 +219,7 @@ private void configureSslForHttp2(SocketChannel ch, ChannelPipeline clientPipeli sslConfig.getCacheSize())); } } - clientPipeline.addLast(new Http2PipelineConfiguratorForClient(targetHandler, connectionAvailabilityFuture)); + clientPipeline.addLast(new Http2PipelineConfiguratorAfterALPNNegotationForClient(targetHandler, connectionAvailabilityFuture)); clientPipeline .addLast(Constants.HTTP2_EXCEPTION_HANDLER, new Http2ExceptionHandler(http2ConnectionHandler)); } @@ -329,12 +329,12 @@ public void setHttp2ClientChannel(Http2ClientChannel http2ClientChannel) { /** * A handler to create the pipeline based on the ALPN negotiated protocol. */ - class Http2PipelineConfiguratorForClient extends ApplicationProtocolNegotiationHandler { + class Http2PipelineConfiguratorAfterALPNNegotationForClient extends ApplicationProtocolNegotiationHandler { private TargetHandler targetHandler; private ConnectionAvailabilityFuture connectionAvailabilityFuture; - public Http2PipelineConfiguratorForClient(TargetHandler targetHandler, + public Http2PipelineConfiguratorAfterALPNNegotationForClient(TargetHandler targetHandler, ConnectionAvailabilityFuture connectionAvailabilityFuture) { super(ApplicationProtocolNames.HTTP_1_1); this.targetHandler = targetHandler; From 6bb85318a1c229d44588306a13d6aeee831f3e5d Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 16:58:22 +0530 Subject: [PATCH 24/46] Catch create stream error --- .../contractimpl/common/states/Http2StateUtil.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java index 55b52c9822..55aada5f5c 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java @@ -371,7 +371,11 @@ private static int getNextStreamId(Http2Connection conn) { * @throws Http2Exception if a protocol-related error occurred */ private static void createStream(Http2Connection conn, int streamId) throws Http2Exception { - conn.local().createStream(streamId, false); + try { + conn.local().createStream(streamId, false); + } catch (Http2Exception exception) { + throw new Http2Exception(exception.error(), "Error occured while creating stream", exception); + } if (LOG.isDebugEnabled()) { LOG.debug("Stream created streamId: {}", streamId); } From 5ee83ddcb69e1183e8b63408177dc99a36609b68 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 18:03:57 +0530 Subject: [PATCH 25/46] Update test cases --- ...estUtils.java => FrameLevelTestUtils.java} | 4 +- ...meoutAfterTcpServerGoAwayScenarioTest.java | 53 +++++------ ...erverGoAwayMultipleStreamScenarioTest.java | 89 +++++++------------ ...pServerGoAwaySingleStreamScenarioTest.java | 50 ++++------- ...rGoAwayWhileReceivingBodyScenarioTest.java | 37 +++----- ...rRSTStreamFrameForMultipleStreamsTest.java | 70 +++++---------- ...rverRSTStreamFrameForSingleStreamTest.java | 34 +++---- ...ameWhenReadingBodyForSingleStreamTest.java | 38 +++----- ...erSendGoAwayForAllStreamsScenarioTest.java | 81 ++++++----------- .../Http2TcpServerSuccessScenarioTest.java | 32 +++---- .../stdlib/http/transport/util/TestUtil.java | 19 +++- 11 files changed, 184 insertions(+), 323 deletions(-) rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/{TestUtils.java => FrameLevelTestUtils.java} (98%) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java similarity index 98% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java index 2d8674347d..8034e9ea97 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/TestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java @@ -29,7 +29,7 @@ /** * This contains the utils required for frame level tests. */ -public class TestUtils { +public class FrameLevelTestUtils { public static final int SLEEP_TIME = 100; public static final int END_SLEEP_TIME = 1000; @@ -49,7 +49,7 @@ public class TestUtils { public static final byte[] DATA_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x33}; public static final byte[] DATA_FRAME_STREAM_03_DIFFERENT_DATA = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x35}; + 0x00, 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x34}; public static final byte[] DATA_FRAME_STREAM_05 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x35}; public static final byte[] DATA_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java index 70173a2924..8eec8b997c 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java @@ -20,7 +20,6 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; @@ -30,8 +29,6 @@ import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpMethod; import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,24 +44,27 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests + .FrameLevelTestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; import static org.testng.Assert.assertEqualsNoOrder; +import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a GoAway and the connection gets timed out from client side. */ public class Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest { - private static final Logger LOGGER = LoggerFactory.getLogger(Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.class); + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; private ServerSocket serverSocket; private int numOfConnections = 0; @@ -91,30 +91,15 @@ public HttpClientConnector setupHttp2PriorKnowledgeClient() { @Test private void testConnectionTimeoutAfterServerGoAwayScenario() { - HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { CountDownLatch latch1 = new CountDownLatch(2); - DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch1); - HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); - responseFuture1.setHttpConnectorListener(msgListener1); - - DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch1); - HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); - responseFuture2.setHttpConnectorListener(msgListener2); - + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture1.sync(); - responseFuture2.sync(); CountDownLatch latch2 = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(latch2); - HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); - responseFuture3.setHttpConnectorListener(msgListener3); - + DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture3.sync(); HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); @@ -123,10 +108,12 @@ private void testConnectionTimeoutAfterServerGoAwayScenario() { Object responseVal1 = response1.getHttpContent().content().toString(CharsetUtil.UTF_8); Object responseVal2 = response2.getHttpContent().content().toString(CharsetUtil.UTF_8); Object responseVal3 = response3.getHttpContent().content().toString(CharsetUtil.UTF_8); + assertEqualsNoOrder(List.of(responseVal1, responseVal2, responseVal3), - List.of("hello world3", "hello world5", "hello world5")); + List.of("hello world3", "hello world4", "hello world5")); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); + fail(); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index e51c89cbe0..8bbdf1a917 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -20,13 +20,8 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterClass; @@ -41,20 +36,23 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_09; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_09; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEqualsNoOrder; +import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a GoAway for a stream in a multiple stream scenario. @@ -68,52 +66,33 @@ public class Http2TcpServerGoAwayMultipleStreamScenarioTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { - HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage4 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + CountDownLatch latch = new CountDownLatch(4); + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener4 = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); try { - CountDownLatch latch = new CountDownLatch(4); - DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); - responseFuture1.setHttpConnectorListener(msgListener1); - DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); - responseFuture2.setHttpConnectorListener(msgListener2); - DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); - responseFuture3.setHttpConnectorListener(msgListener3); - DefaultHttpConnectorListener msgListener4 = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture4 = h2ClientWithPriorKnowledge.send(httpCarbonMessage4); - responseFuture4.setHttpConnectorListener(msgListener4); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture1.sync(); - responseFuture2.sync(); - responseFuture3.sync(); - responseFuture4.sync(); - HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); - HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); - HttpCarbonMessage response3 = msgListener3.getHttpResponseMessage(); - HttpCarbonMessage response4 = msgListener4.getHttpResponseMessage(); - Object responseValOrError1 = response1 == null ? responseFuture1.getStatus().getCause().getMessage() : - response1.getHttpContent().content().toString(CharsetUtil.UTF_8); - Object responseValOrError2 = response2 == null ? responseFuture2.getStatus().getCause().getMessage() : - response2.getHttpContent().content().toString(CharsetUtil.UTF_8); - Object responseValOrError3 = response3 == null ? responseFuture3.getStatus().getCause().getMessage() : - response3.getHttpContent().content().toString(CharsetUtil.UTF_8); - Object responseValOrError4 = response4 == null ? responseFuture4.getStatus().getCause().getMessage() : - response4.getHttpContent().content().toString(CharsetUtil.UTF_8); - assertEqualsNoOrder(List.of(responseValOrError1, responseValOrError2, responseValOrError3, - responseValOrError4), List.of("hello world3", "hello world5", "hello world7", - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } + + Object responseValOrError1 = msgListener1.getHttpResponseMessage() == null ? + getErrorResponseMessage(msgListener1) : getResponseMessage(msgListener1); + Object responseValOrError2 = msgListener2.getHttpResponseMessage() == null ? + getErrorResponseMessage(msgListener2) : getResponseMessage(msgListener2); + Object responseValOrError3 = msgListener3.getHttpResponseMessage() == null ? + getErrorResponseMessage(msgListener3) : getResponseMessage(msgListener3); + Object responseValOrError4 = msgListener4.getHttpResponseMessage() == null ? + getErrorResponseMessage(msgListener4) : getResponseMessage(msgListener4); + assertEqualsNoOrder(List.of(responseValOrError1, responseValOrError2, responseValOrError3, + responseValOrError4), List.of("hello world3", "hello world5", "hello world7", + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java index b6d2a72657..d10128e185 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -20,14 +20,8 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -42,13 +36,15 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -63,22 +59,17 @@ public class Http2TcpServerGoAwaySingleStreamScenarioTest { @BeforeMethod public void setup(Method method) throws InterruptedException { - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test (description = "In this, server sends headers and data for the accepted stream") private void testGoAwayWhenReceivingHeadersForASingleStream() { runTcpServer(TestUtil.HTTP_SERVER_PORT, 1); - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); - assertEquals(content.content().toString(CharsetUtil.UTF_8), "hello world3"); + assertEquals(getResponseMessage(msgListener), "hello world3"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } @@ -87,24 +78,15 @@ private void testGoAwayWhenReceivingHeadersForASingleStream() { @Test (description = "In this, server exits without sending the headers and data for the accepted stream as well") private void testGoAwayAndServerExitWhenReceivingHeadersForASingleStream() { runTcpServer(TestUtil.HTTP_SERVER_PORT, 2); - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); try { - CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - Throwable responseError = responseFuture.getStatus().getCause(); - if (responseError != null) { - assertEquals(responseError.getMessage(), - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); - } else { - fail("Expected error not received"); - } } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } + assertEquals(getErrorResponseMessage(msgListener), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } private void runTcpServer(int port, int option) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index 5d56ab140d..90e6e594f0 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -20,13 +20,8 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -40,12 +35,13 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_01; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_01; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getDecoderErrorMessage; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -62,29 +58,20 @@ public class Http2TcpServerGoAwayWhileReceivingBodyScenarioTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testGoAwayWhenReceivingBodyForASingleStream() { - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); try { - CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); - if (content != null) { - assertEquals(content.decoderResult().cause().getMessage(), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); - } else { - fail("Expected http content"); - } } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } + assertEquals(getDecoderErrorMessage(msgListener), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index f6e8af0780..e6baab337a 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -20,14 +20,8 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -38,18 +32,20 @@ import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_07; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.RST_STREAM_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.RST_STREAM_FRAME_STREAM_07; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getDecoderErrorMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -68,51 +64,29 @@ public class Http2TcpServerRSTStreamFrameForMultipleStreamsTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testRSTStreamFrameForMultipleStreams() { - HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { - DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(new CountDownLatch(1)); - HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); - responseFuture1.setHttpConnectorListener(msgListener1); + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); writeSemaphore.release(); readSemaphore.acquire(); - DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(new CountDownLatch(1)); - HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); - responseFuture2.setHttpConnectorListener(msgListener2); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); writeSemaphore.release(); readSemaphore.acquire(); - DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(new CountDownLatch(1)); - HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); - responseFuture3.setHttpConnectorListener(msgListener3); + DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); writeSemaphore.release(); readSemaphore.acquire(); - responseFuture1.sync(); - responseFuture2.sync(); - responseFuture3.sync(); - Throwable throwable = responseFuture1.getStatus().getCause(); - if (throwable != null) { - assertEquals(throwable.getMessage(), - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); - } else { - fail("Expected an error"); - } - HttpContent content2 = msgListener2.getHttpResponseMessage().getHttpContent(); - assertEquals(content2.content().toString(CharsetUtil.UTF_8), "hello world5"); - HttpContent content3 = msgListener3.getHttpResponseMessage().getHttpContent(); - if (content3 != null) { - assertEquals(content3.decoderResult().cause().getMessage(), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); - } else { - fail("Expected http content"); - } + assertEquals(getErrorResponseMessage(msgListener1), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + assertEquals(getResponseMessage(msgListener2), "hello world5"); + assertEquals(getDecoderErrorMessage(msgListener3), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); + fail(); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index c9bce5139b..0285256cf6 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -20,12 +20,8 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -39,11 +35,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.RST_STREAM_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -59,29 +56,20 @@ public class Http2TcpServerRSTStreamFrameForSingleStreamTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testRSTStreamFrameForSingleStream() { - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); try { - CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - Throwable throwable = responseFuture.getStatus().getCause(); - if (throwable != null) { - assertEquals(throwable.getMessage(), - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); - } else { - fail("Expected an error"); - } } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } + assertEquals(getResponseMessage(msgListener), + Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index 2d803c0d89..6152a0f4dd 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -20,13 +20,8 @@ import io.ballerina.stdlib.http.transport.contract.Constants; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -40,12 +35,13 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.RST_STREAM_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.RST_STREAM_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getDecoderErrorMessage; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -62,29 +58,21 @@ public class Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testRSTStreamFrameWhenReadingBodyForSingleStream() { - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); try { - CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); - if (content != null) { - assertEquals(content.decoderResult().cause().getMessage(), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); - } else { - fail("Expected http content"); - } } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } + assertEquals(getDecoderErrorMessage(msgListener), + Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java index 6c92aab9c4..71834a372f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java @@ -19,13 +19,8 @@ package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -38,15 +33,18 @@ import java.net.Socket; import java.util.concurrent.Semaphore; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.GO_AWAY_FRAME_MAX_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils + .DATA_FRAME_STREAM_03_DIFFERENT_DATA; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; /** * This contains a test case where the client sends a request after receiving a GoAway. @@ -64,62 +62,33 @@ public class Http2TcpServerSendGoAwayForAllStreamsScenarioTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testGoAwayForAllStreamsScenario() { - HttpCarbonMessage httpCarbonMessage1 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage2 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage3 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage4 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage5 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); - HttpCarbonMessage httpCarbonMessage6 = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); try { - DefaultHttpConnectorListener msgListener1 = new DefaultHttpConnectorListener(); - HttpResponseFuture responseFuture1 = h2ClientWithPriorKnowledge.send(httpCarbonMessage1); - responseFuture1.setHttpConnectorListener(msgListener1); + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - responseFuture1.sync(); - DefaultHttpConnectorListener msgListener2 = new DefaultHttpConnectorListener(); - HttpResponseFuture responseFuture2 = h2ClientWithPriorKnowledge.send(httpCarbonMessage2); - responseFuture2.setHttpConnectorListener(msgListener2); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - responseFuture2.sync(); - DefaultHttpConnectorListener msgListener3 = new DefaultHttpConnectorListener(); - HttpResponseFuture responseFuture3 = h2ClientWithPriorKnowledge.send(httpCarbonMessage3); - responseFuture3.setHttpConnectorListener(msgListener3); + DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - responseFuture3.sync(); - DefaultHttpConnectorListener msgListener4 = new DefaultHttpConnectorListener(); - HttpResponseFuture responseFuture4 = h2ClientWithPriorKnowledge.send(httpCarbonMessage4); - responseFuture4.setHttpConnectorListener(msgListener4); + DefaultHttpConnectorListener msgListener4 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - responseFuture4.sync(); - DefaultHttpConnectorListener msgListener5 = new DefaultHttpConnectorListener(); - HttpResponseFuture responseFuture5 = h2ClientWithPriorKnowledge.send(httpCarbonMessage5); - responseFuture5.setHttpConnectorListener(msgListener5); + DefaultHttpConnectorListener msgListener5 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - responseFuture5.sync(); - DefaultHttpConnectorListener msgListener6 = new DefaultHttpConnectorListener(); - HttpResponseFuture responseFuture6 = h2ClientWithPriorKnowledge.send(httpCarbonMessage6); - responseFuture6.setHttpConnectorListener(msgListener6); + DefaultHttpConnectorListener msgListener6 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - responseFuture6.sync(); - HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); - assertEquals(response1.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); - HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); - assertEquals(response2.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); - HttpCarbonMessage response3 = msgListener3.getHttpResponseMessage(); - assertEquals(response3.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); - HttpCarbonMessage response4 = msgListener4.getHttpResponseMessage(); - assertEquals(response4.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); - HttpCarbonMessage response5 = msgListener5.getHttpResponseMessage(); - assertEquals(response5.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world3"); - HttpCarbonMessage response6 = msgListener6.getHttpResponseMessage(); - assertEquals(response6.getHttpContent().content().toString(CharsetUtil.UTF_8), "hello world5"); + assertEquals(getResponseMessage(msgListener1), "hello world3"); + assertEquals(getResponseMessage(msgListener2), "hello world4"); + assertEquals(getResponseMessage(msgListener3), "hello world3"); + assertEquals(getResponseMessage(msgListener4), "hello world4"); + assertEquals(getResponseMessage(msgListener5), "hello world3"); + assertEquals(getResponseMessage(msgListener6), "hello world4"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); + fail(); } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java index e6dd0cdf0b..4dd8964ee7 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java @@ -19,14 +19,8 @@ package io.ballerina.stdlib.http.transport.http2.frameleveltests; import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; -import io.ballerina.stdlib.http.transport.contract.HttpResponseFuture; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.ballerina.stdlib.http.transport.util.client.http2.MessageGenerator; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; @@ -40,12 +34,13 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.TestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEquals; /** @@ -60,24 +55,19 @@ public class Http2TcpServerSuccessScenarioTest { @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = TestUtils.setupHttp2PriorKnowledgeClient(); + h2ClientWithPriorKnowledge = FrameLevelTestUtils.setupHttp2PriorKnowledgeClient(); } @Test private void testSuccessfulConnection() { - HttpCarbonMessage httpCarbonMessage = MessageGenerator.generateRequest(HttpMethod.POST, "Test Http2 Message"); + CountDownLatch latch = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); try { - CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = new DefaultHttpConnectorListener(latch); - HttpResponseFuture responseFuture = h2ClientWithPriorKnowledge.send(httpCarbonMessage); - responseFuture.setHttpConnectorListener(msgListener); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - responseFuture.sync(); - HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); - assertEquals(content.content().toString(CharsetUtil.UTF_8), "hello world3"); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } + assertEquals(getResponseMessage(msgListener), "hello world3"); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/util/TestUtil.java b/native/src/test/java/io/ballerina/stdlib/http/transport/util/TestUtil.java index 66f2f8057e..16db2f00f2 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/util/TestUtil.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/util/TestUtil.java @@ -37,8 +37,10 @@ import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -265,7 +267,8 @@ public static DefaultHttpConnectorListener sendRequestAsync(CountDownLatch latch HttpClientConnector httpClientConnector) { HttpCarbonMessage httpsPostReq = TestUtil. createHttpsPostReq(TestUtil.HTTP_SERVER_PORT, "hello", "/"); - DefaultHttpConnectorListener requestListener = new DefaultHttpConnectorListener(latch); + DefaultHttpConnectorListener requestListener = latch == null ? new DefaultHttpConnectorListener() : + new DefaultHttpConnectorListener(latch); HttpResponseFuture responseFuture = httpClientConnector.send(httpsPostReq); responseFuture.setHttpConnectorListener(requestListener); return requestListener; @@ -281,6 +284,20 @@ public static DefaultHttpConnectorListener sendRequestAsyncWithGivenPort(CountDo return requestListener; } + public static String getResponseMessage(DefaultHttpConnectorListener msgListener) { + HttpCarbonMessage response = msgListener.getHttpResponseMessage(); + return response.getHttpContent().content().toString(CharsetUtil.UTF_8); + } + + public static String getErrorResponseMessage(DefaultHttpConnectorListener msgListener) { + return msgListener.getHttpErrorMessage().getMessage(); + } + + public static String getDecoderErrorMessage(DefaultHttpConnectorListener msgListener) { + HttpContent content = msgListener.getHttpResponseMessage().getHttpContent(); + return content.decoderResult().cause().getMessage(); + } + public static void cleanUp(List serverConnectors, HttpServer httpServer) { for (ServerConnector httpServerConnector : serverConnectors) { if (!httpServerConnector.stop()) { From 9a239bbd75488da2aa7439dcc723fa5c206a0893 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 18:49:18 +0530 Subject: [PATCH 26/46] Remove goaway received connections from channelpool --- .../sender/channel/pool/PoolableTargetChannelFactory.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java index 7ce0e656ea..a07c9b2fe0 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java @@ -152,6 +152,10 @@ public void destroyObject(Object o) throws Exception { @Override public boolean validateObject(Object o) { TargetChannel targetChannel = (TargetChannel) o; + if (targetChannel.getHttp2ClientChannel() != null && + targetChannel.getHttp2ClientChannel().getConnection().goAwayReceived()) { + return false; + } if (targetChannel.getChannel() != null) { boolean answer = targetChannel.getChannel().isActive(); LOG.debug("Validating channel: {} -> {}", targetChannel.getChannel().id(), answer); From d570f07a7409e0361b785090ecfca5193f8f5af3 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 22:25:01 +0530 Subject: [PATCH 27/46] Remove redundant configs --- .../sender/channel/pool/ConnectionManager.java | 2 +- .../sender/channel/pool/PoolConfiguration.java | 9 --------- .../sender/http2/Http2ConnectionManager.java | 5 +++-- ...onnectionTimeoutAfterTcpServerGoAwayScenarioTest.java | 3 ++- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/ConnectionManager.java index 4bea045824..5fa329abe9 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/ConnectionManager.java @@ -52,7 +52,7 @@ public ConnectionManager(PoolConfiguration poolConfiguration) { globalConnPool = new ConcurrentHashMap<>(); globalFactoryObjects = new ConcurrentHashMap<>(); http2ConnectionManager = new Http2ConnectionManager(poolConfiguration); - connectionManagerId = "-" + UUID.randomUUID().toString(); + connectionManagerId = "-" + UUID.randomUUID(); } /** diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java index d14c47fb67..9ab58dc6d0 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java @@ -35,7 +35,6 @@ public class PoolConfiguration { private int eventGroupExecutorThreads = 15; private long maxWaitTime = 60000L; private int http2MaxActiveStreamsPerConnection = Integer.MAX_VALUE; - private long http2ConnectionIdleTimeout = 30000; public PoolConfiguration() { } @@ -143,12 +142,4 @@ public int getHttp2MaxActiveStreamsPerConnection() { public void setHttp2MaxActiveStreamsPerConnection(int http2MaxActiveStreamsPerConnection) { this.http2MaxActiveStreamsPerConnection = http2MaxActiveStreamsPerConnection; } - - public long getHttp2ConnectionIdleTimeout() { - return http2ConnectionIdleTimeout; - } - - public void setHttp2ConnectionIdleTimeout(long http2ConnectionIdleTimeout) { - this.http2ConnectionIdleTimeout = http2ConnectionIdleTimeout; - } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index 694bfd9919..4b34494698 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -179,7 +179,7 @@ private void initiateConnectionTimeoutTask() { public void run() { http2StaleClientChannels.forEach(http2ClientChannel -> { if ((System.currentTimeMillis() - http2ClientChannel.getTimeSinceMarkedAsStale()) > - poolConfiguration.getHttp2ConnectionIdleTimeout() + poolConfiguration.getMinEvictableIdleTime() && !http2ClientChannel.hasInFlightMessages()) { http2StaleClientChannels.remove(http2ClientChannel); http2ClientChannel.getConnection() @@ -188,7 +188,8 @@ public void run() { }); } }; - timer.schedule(timerTask, 10000, 30000); + timer.schedule(timerTask, poolConfiguration.getTimeBetweenEvictionRuns(), + poolConfiguration.getTimeBetweenEvictionRuns()); } private Http2ChannelPool.PerRouteConnectionPool fetchPerRoutePool(HttpRoute httpRoute) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java index 8eec8b997c..43d9885689 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java @@ -78,7 +78,8 @@ public void setup() throws InterruptedException { public HttpClientConnector setupHttp2PriorKnowledgeClient() { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); PoolConfiguration poolConfiguration = new PoolConfiguration(); - poolConfiguration.setHttp2ConnectionIdleTimeout(5000); + poolConfiguration.setMinEvictableIdleTime(5000); + poolConfiguration.setTimeBetweenEvictionRuns(1000); TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); SenderConfiguration senderConfiguration = new SenderConfiguration(); senderConfiguration.setPoolConfiguration(poolConfiguration); From a6006f2a411114f42959de245141c3ce25b67ac6 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 22:31:03 +0530 Subject: [PATCH 28/46] Remove redundant pool object validation --- .../sender/channel/pool/PoolableTargetChannelFactory.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java index a07c9b2fe0..7ce0e656ea 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolableTargetChannelFactory.java @@ -152,10 +152,6 @@ public void destroyObject(Object o) throws Exception { @Override public boolean validateObject(Object o) { TargetChannel targetChannel = (TargetChannel) o; - if (targetChannel.getHttp2ClientChannel() != null && - targetChannel.getHttp2ClientChannel().getConnection().goAwayReceived()) { - return false; - } if (targetChannel.getChannel() != null) { boolean answer = targetChannel.getChannel().isActive(); LOG.debug("Validating channel: {} -> {}", targetChannel.getChannel().id(), answer); From 197fbcc390e60e480e3b3ae29963e566c32c333b Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 22:35:30 +0530 Subject: [PATCH 29/46] Fix line length --- .../contractimpl/sender/HttpClientChannelInitializer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java index 6bf4c22d32..9e5fbd5b19 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java @@ -219,7 +219,8 @@ private void configureSslForHttp2(SocketChannel ch, ChannelPipeline clientPipeli sslConfig.getCacheSize())); } } - clientPipeline.addLast(new Http2PipelineConfiguratorAfterALPNNegotationForClient(targetHandler, connectionAvailabilityFuture)); + clientPipeline.addLast( + new Http2PipelineConfiguratorAfterALPNNegotationForClient(targetHandler, connectionAvailabilityFuture)); clientPipeline .addLast(Constants.HTTP2_EXCEPTION_HANDLER, new Http2ExceptionHandler(http2ConnectionHandler)); } From ceaad83f86c569c464f238e823b846fc00fa6001 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 22:38:55 +0530 Subject: [PATCH 30/46] Update test case name --- ...ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java} | 6 +++--- native/src/test/resources/testng.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/{Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java => Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java} (97%) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java similarity index 97% rename from native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java rename to native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java index 43d9885689..0387057a65 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java @@ -61,10 +61,10 @@ /** * This contains a test case where the tcp server sends a GoAway and the connection gets timed out from client side. */ -public class Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest { +public class Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest { private static final Logger LOGGER = - LoggerFactory.getLogger(Http2ConnectionTimeoutAfterTcpServerGoAwayScenarioTest.class); + LoggerFactory.getLogger(Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.class); private HttpClientConnector h2ClientWithPriorKnowledge; private ServerSocket serverSocket; private int numOfConnections = 0; @@ -91,7 +91,7 @@ public HttpClientConnector setupHttp2PriorKnowledgeClient() { } @Test - private void testConnectionTimeoutAfterServerGoAwayScenario() { + private void testConnectionEvictionAfterServerGoAwayScenario() { try { CountDownLatch latch1 = new CountDownLatch(2); DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index 51418dbce6..8cd178175e 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -162,7 +162,7 @@ - + From 7facc8553e54f86c2d57ddc8ffa3bfc2ae4b3e44 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 23:28:06 +0530 Subject: [PATCH 31/46] Improve codebase --- .../sender/http2/Http2ClientChannel.java | 15 ++++++------- .../sender/http2/Http2ConnectionManager.java | 21 ++++++------------- ...erverGoAwayMultipleStreamScenarioTest.java | 1 - ...pServerGoAwaySingleStreamScenarioTest.java | 1 - ...rGoAwayWhileReceivingBodyScenarioTest.java | 1 - ...rverRSTStreamFrameForSingleStreamTest.java | 5 ++--- ...ameWhenReadingBodyForSingleStreamTest.java | 1 - 7 files changed, 16 insertions(+), 29 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index d6213e6d31..89e65eab87 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -62,6 +62,7 @@ public class Http2ClientChannel { private Map dataEventListeners; private StreamCloseListener streamCloseListener; private long timeSinceMarkedAsStale = 0; + private AtomicBoolean isStale = new AtomicBoolean(false); public Http2ClientChannel(Http2ConnectionManager http2ConnectionManager, Http2Connection connection, HttpRoute httpRoute, Channel channel) { @@ -291,17 +292,16 @@ public void onStreamClosed(Http2Stream stream) { activeStreams.decrementAndGet(); http2ClientChannel.getDataEventListeners(). forEach(dataEventListener -> dataEventListener.onStreamClose(stream.id())); - if (!isExhausted.get() && isExhausted.getAndSet(false)) { + if (!isStale.get() && isExhausted.getAndSet(false)) { http2ConnectionManager.returnClientChannel(httpRoute, http2ClientChannel); } } @Override public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - markAsExhausted(); - http2ConnectionManager.getLock().lock(); - markAsStale(); - http2ConnectionManager.getLock().unlock(); + synchronized (http2ConnectionManager) { + markAsStale(); + } http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { if (streamId > lastStreamId) { http2ClientChannel.removeInFlightMessage(streamId); @@ -323,6 +323,7 @@ void removeFromConnectionPool() { } void markAsStale() { + isStale.set(true); http2ConnectionManager.markClientChannelAsStale(httpRoute, this); } @@ -330,11 +331,11 @@ boolean hasInFlightMessages() { return !inFlightMessages.isEmpty(); } - public void setTimeSinceMarkedAsStale(long timeSinceMarkedAsStale) { + void setTimeSinceMarkedAsStale(long timeSinceMarkedAsStale) { this.timeSinceMarkedAsStale = timeSinceMarkedAsStale; } - public long getTimeSinceMarkedAsStale() { + long getTimeSinceMarkedAsStale() { return timeSinceMarkedAsStale; } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index 4b34494698..504d33265c 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -20,14 +20,13 @@ import io.ballerina.stdlib.http.transport.contractimpl.common.HttpRoute; import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; -import io.netty.channel.DefaultChannelPromise; +import io.netty.channel.DefaultEventLoop; +import io.netty.util.concurrent.DefaultPromise; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * {@code Http2ConnectionManager} Manages HTTP/2 connections. @@ -37,11 +36,10 @@ public class Http2ConnectionManager { private final Http2ChannelPool http2ChannelPool = new Http2ChannelPool(); private final BlockingQueue http2StaleClientChannels = new LinkedBlockingQueue<>(); private final PoolConfiguration poolConfiguration; - private Lock lock = new ReentrantLock(); public Http2ConnectionManager(PoolConfiguration poolConfiguration) { this.poolConfiguration = poolConfiguration; - initiateConnectionTimeoutTask(); + initiateConnectionEvictionTask(); } /** @@ -123,8 +121,7 @@ private synchronized Http2ChannelPool.PerRouteConnectionPool createPerRouteConne */ public Http2ClientChannel fetchChannel(HttpRoute httpRoute) { Http2ChannelPool.PerRouteConnectionPool perRouteConnectionPool; - try { - getLock().lock(); + synchronized (this) { perRouteConnectionPool = getOrCreatePerRoutePool(this.http2ChannelPool, generateKey(httpRoute)); Http2ClientChannel http2ClientChannel = null; @@ -132,8 +129,6 @@ public Http2ClientChannel fetchChannel(HttpRoute httpRoute) { http2ClientChannel = perRouteConnectionPool.fetchTargetChannel(); } return http2ClientChannel; - } finally { - getLock().unlock(); } } @@ -172,7 +167,7 @@ void markClientChannelAsStale(HttpRoute httpRoute, Http2ClientChannel http2Clien http2StaleClientChannels.add(http2ClientChannel); } - private void initiateConnectionTimeoutTask() { + private void initiateConnectionEvictionTask() { Timer timer = new Timer(true); TimerTask timerTask = new TimerTask() { @Override @@ -183,7 +178,7 @@ public void run() { && !http2ClientChannel.hasInFlightMessages()) { http2StaleClientChannels.remove(http2ClientChannel); http2ClientChannel.getConnection() - .close(new DefaultChannelPromise(http2ClientChannel.getChannel())); + .close(new DefaultPromise(new DefaultEventLoop())); } }); } @@ -201,8 +196,4 @@ private String generateKey(HttpRoute httpRoute) { return httpRoute.getScheme() + ":" + httpRoute.getHost() + ":" + httpRoute.getPort() + ":" + httpRoute.getConfigHash(); } - - public Lock getLock() { - return lock; - } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index 8bbdf1a917..428b46f0a2 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -52,7 +52,6 @@ import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEqualsNoOrder; -import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a GoAway for a stream in a multiple stream scenario. diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java index d10128e185..14c9e04c77 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -46,7 +46,6 @@ import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a GoAway for a single request. diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index 90e6e594f0..3639e07f07 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -43,7 +43,6 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; import static io.ballerina.stdlib.http.transport.util.TestUtil.getDecoderErrorMessage; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a GoAway while client receives the body. diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index 0285256cf6..d635f72a35 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -40,9 +40,8 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a successful response. @@ -68,7 +67,7 @@ private void testRSTStreamFrameForSingleStream() { } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } - assertEquals(getResponseMessage(msgListener), + assertEquals(getErrorResponseMessage(msgListener), Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index 6152a0f4dd..9c5f64b3ce 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -43,7 +43,6 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; import static io.ballerina.stdlib.http.transport.util.TestUtil.getDecoderErrorMessage; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; /** * This contains a test case where the tcp server sends a successful response. From a297fcc47e2124c87ca669984c383c9ab4bcfa9d Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 17 Jan 2024 23:39:10 +0530 Subject: [PATCH 32/46] Update changelog.md --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 5254d809db..8bbe375053 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - [Expose HTTP connection eviction configurations in the client level](https://github.com/ballerina-platform/ballerina-library/issues/5951) +- [Added a way to handle GoAway frames in HTTP/2 clients](https://github.com/ballerina-platform/ballerina-library/issues/4806) ## [2.10.5] - 2023-12-06 From 60bd19bab4d8af1479eaaed7811b2a25b341bb4a Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Thu, 18 Jan 2024 18:57:31 +0530 Subject: [PATCH 33/46] Update test cases and implementation --- .../contractimpl/common/states/Http2StateUtil.java | 2 +- .../contractimpl/sender/http2/Http2ChannelPool.java | 6 +++--- .../contractimpl/sender/http2/Http2ClientChannel.java | 10 +++++----- .../http2/TestExhaustedStreamIdForClient.java | 4 +++- ...2TcpServerRSTStreamFrameForMultipleStreamsTest.java | 2 ++ ...ttp2TcpServerRSTStreamFrameForSingleStreamTest.java | 2 +- ...TStreamFrameWhenReadingBodyForSingleStreamTest.java | 2 +- .../Http2TcpServerSuccessScenarioTest.java | 2 +- 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java index 55aada5f5c..449f01b3db 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java @@ -374,7 +374,7 @@ private static void createStream(Http2Connection conn, int streamId) throws Http try { conn.local().createStream(streamId, false); } catch (Http2Exception exception) { - throw new Http2Exception(exception.error(), "Error occured while creating stream", exception); + throw new Http2Exception(exception.error(), "Error occurred while creating stream", exception); } if (LOG.isDebugEnabled()) { LOG.debug("Stream created streamId: {}", streamId); diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ChannelPool.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ChannelPool.java index bb77cc2259..baf4786251 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ChannelPool.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ChannelPool.java @@ -79,7 +79,7 @@ synchronized Http2ClientChannel fetchTargetChannel() { } Channel channel = http2ClientChannel.getChannel(); if (channel == null) { // if channel is not active, forget it and fetch next one - http2ClientChannels.remove(http2ClientChannel); + removeChannel(http2ClientChannel); return fetchTargetChannel(); } // increment and get active stream count @@ -89,7 +89,7 @@ synchronized Http2ClientChannel fetchTargetChannel() { return http2ClientChannel; } else if (activeStreamCount == maxActiveStreams) { // no more streams except this one can be opened http2ClientChannel.markAsExhausted(); - http2ClientChannels.remove(http2ClientChannel); + removeChannel(http2ClientChannel); // When the stream count reaches maxActiveStreams, a new channel will be added only if the // channel queue is empty. This process is synchronized as transport thread can return channels // after being reset. If such channel is returned before the new CountDownLatch, the subsequent @@ -104,7 +104,7 @@ synchronized Http2ClientChannel fetchTargetChannel() { } return http2ClientChannel; } else { - http2ClientChannels.remove(http2ClientChannel); + removeChannel(http2ClientChannel); return fetchTargetChannel(); // fetch the next one from the queue } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 89e65eab87..5c29cc34c8 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -299,9 +299,7 @@ public void onStreamClosed(Http2Stream stream) { @Override public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - synchronized (http2ConnectionManager) { - markAsStale(); - } + markAsStale(); http2ClientChannel.inFlightMessages.forEach((streamId, outboundMsgHolder) -> { if (streamId > lastStreamId) { http2ClientChannel.removeInFlightMessage(streamId); @@ -323,8 +321,10 @@ void removeFromConnectionPool() { } void markAsStale() { - isStale.set(true); - http2ConnectionManager.markClientChannelAsStale(httpRoute, this); + synchronized (http2ConnectionManager) { + isStale.set(true); + http2ConnectionManager.markClientChannelAsStale(httpRoute, this); + } } boolean hasInFlightMessages() { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java index c46f1ee591..767b9583d9 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java @@ -110,8 +110,10 @@ public void testExhaustedStreamId() throws Http2Exception { HttpCarbonMessage secondMessage = MessageGenerator.generateRequest(HttpMethod.POST, testValue); Throwable firstError = new MessageSender(httpClientConnector).sendMessageAndExpectError(secondMessage); assertNotNull(firstError, "Expected error not received"); - assertEquals(firstError.getMessage(), "No more streams can be created on this connection", + assertEquals(firstError.getMessage(), "Error occurred while creating stream", "Expected error response not received"); + assertEquals(firstError.getCause().getMessage(), "No more streams can be created on this connection", + "Expected error response not received"); //Send another request using the same client and it should not fail HttpCarbonMessage thirdMessage = MessageGenerator.generateRequest(HttpMethod.POST, testValue); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index e6baab337a..b12286edbf 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -130,7 +130,9 @@ private void sendRSTStream(OutputStream outputStream) throws IOException, Interr readSemaphore.release(); writeSemaphore.acquire(); outputStream.write(HEADER_FRAME_STREAM_07); + Thread.sleep(SLEEP_TIME); outputStream.write(RST_STREAM_FRAME_STREAM_07); + Thread.sleep(SLEEP_TIME); readSemaphore.release(); Thread.sleep(END_SLEEP_TIME); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index d635f72a35..a3c0800263 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -61,7 +61,7 @@ public void setup() throws InterruptedException { @Test private void testRSTStreamFrameForSingleStream() { CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); try { latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); } catch (InterruptedException e) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index 9c5f64b3ce..6d6d0befdc 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -63,7 +63,7 @@ public void setup() throws InterruptedException { @Test private void testRSTStreamFrameWhenReadingBodyForSingleStream() { CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); try { latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); } catch (InterruptedException e) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java index 4dd8964ee7..ca776ee168 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java @@ -61,7 +61,7 @@ public void setup() throws InterruptedException { @Test private void testSuccessfulConnection() { CountDownLatch latch = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); try { latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); } catch (InterruptedException e) { From dc64c1f6e1b0708bbea032d1852a278e15fa58c1 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Thu, 18 Jan 2024 23:35:08 +0530 Subject: [PATCH 34/46] Fix test case failure --- .../transport/contractimpl/common/states/Http2StateUtil.java | 2 +- .../http/transport/http2/TestExhaustedStreamIdForClient.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java index 449f01b3db..859af7148d 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/common/states/Http2StateUtil.java @@ -373,7 +373,7 @@ private static int getNextStreamId(Http2Connection conn) { private static void createStream(Http2Connection conn, int streamId) throws Http2Exception { try { conn.local().createStream(streamId, false); - } catch (Http2Exception exception) { + } catch (Http2Exception.StreamException exception) { throw new Http2Exception(exception.error(), "Error occurred while creating stream", exception); } if (LOG.isDebugEnabled()) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java index 767b9583d9..fb62bf1ab4 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/TestExhaustedStreamIdForClient.java @@ -110,9 +110,7 @@ public void testExhaustedStreamId() throws Http2Exception { HttpCarbonMessage secondMessage = MessageGenerator.generateRequest(HttpMethod.POST, testValue); Throwable firstError = new MessageSender(httpClientConnector).sendMessageAndExpectError(secondMessage); assertNotNull(firstError, "Expected error not received"); - assertEquals(firstError.getMessage(), "Error occurred while creating stream", - "Expected error response not received"); - assertEquals(firstError.getCause().getMessage(), "No more streams can be created on this connection", + assertEquals(firstError.getMessage(), "No more streams can be created on this connection", "Expected error response not received"); //Send another request using the same client and it should not fail From 2c024cc1d0bd9df74324f7c972b3528754ed4556 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 19 Jan 2024 00:54:59 +0530 Subject: [PATCH 35/46] Add goaway rststream events to the state machine --- .../http/transport/contract/Constants.java | 30 +++++++++++++++++++ .../sender/http2/Http2ClientChannel.java | 2 +- .../sender/http2/Http2TargetHandler.java | 2 +- .../states/http2/EntityBodyReceived.java | 10 +++++++ .../states/http2/ReceivingEntityBody.java | 14 +++++++++ .../sender/states/http2/ReceivingHeaders.java | 14 +++++++++ .../sender/states/http2/RequestCompleted.java | 14 +++++++++ .../sender/states/http2/SenderState.java | 11 +++++++ .../states/http2/SendingEntityBody.java | 21 +++++++++++++ .../sender/states/http2/SendingHeaders.java | 17 +++++++++++ .../sender/states/http2/SendingRstFrame.java | 29 ++++++++++++++---- .../states/http2/WaitingFor100Continue.java | 13 ++++++++ ...erverGoAwayMultipleStreamScenarioTest.java | 2 +- ...rGoAwayWhileReceivingBodyScenarioTest.java | 2 +- ...rRSTStreamFrameForMultipleStreamsTest.java | 4 +-- ...rverRSTStreamFrameForSingleStreamTest.java | 2 +- ...ameWhenReadingBodyForSingleStreamTest.java | 2 +- 17 files changed, 175 insertions(+), 14 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java index a99caa264e..22452b994e 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java @@ -345,6 +345,8 @@ public final class Constants { = "Idle timeout triggered before initiating outbound request"; public static final String IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS = "Idle timeout triggered while writing outbound request headers"; + public static final String IDLE_TIMEOUT_TRIGGERED_WHILE_SENDING_RST_STREAM + = "Idle timeout triggered while sending RSTStream frame"; public static final String IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_BODY = "Idle timeout triggered while writing outbound request entity body"; public static final String IDLE_TIMEOUT_TRIGGERED_BEFORE_INITIATING_INBOUND_RESPONSE @@ -386,6 +388,8 @@ public final class Constants { = "Remote host closed the connection before initiating outbound request"; public static final String REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS = "Remote host closed the connection while writing outbound request headers"; + public static final String REMOTE_SERVER_CLOSED_WHILE_SENDING_RST_STREAM + = "Remote host closed the connection while sending RSTStream frame"; public static final String REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_BODY = "Remote host closed the connection while writing outbound request entity body"; public static final String REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE @@ -396,6 +400,32 @@ public final class Constants { = "Remote host closed the connection while reading inbound response body"; public static final String REMOTE_SERVER_CLOSED_BEFORE_READING_100_CONTINUE_RESPONSE = "Remote host closed the connection before reading 100 continue response"; + // Server GoAway error scenarios + public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS + = "Remote host sent GoAway while writing outbound request headers"; + public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_SENDING_RST_STREAM + = "Remote host sent GoAway while sending RSTStream frame"; + public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_BODY + = "Remote host sent GoAway while writing outbound request entity body"; + public static final String REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE + = "Remote host sent GoAway before initiating inbound response"; + public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS + = "Remote host sent GoAway while reading inbound response headers"; + public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_BODY + = "Remote host sent GoAway while reading inbound response body"; + // Server send RSTStream error scenarios + public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS + = "Remote host sent RSTStream while writing outbound request headers"; + public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_SENDING_RST_STREAM + = "Remote host sent RSTStream while sending RSTStream frame"; + public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY + = "Remote host sent RSTStream while writing outbound request entity body"; + public static final String REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE + = "Remote host sent RSTStream before initiating inbound response"; + public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS + = "Remote host sent RSTStream while reading inbound response headers"; + public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY + = "Remote host sent RSTStream while reading inbound response body"; public static final String REMOTE_CLIENT_TO_HOST_CONNECTION_CLOSED = "Connection between remote client and host is closed"; diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 5c29cc34c8..47d54ea6ca 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -309,7 +309,7 @@ public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData Http2MessageStateContext messageStateContext = outboundMsgHolder.getRequest().getHttp2MessageStateContext(); if (messageStateContext != null) { - messageStateContext.getSenderState().handleConnectionClose(outboundMsgHolder); + messageStateContext.getSenderState().handleServerGoAway(outboundMsgHolder); } } }); diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java index 14d1f56921..b1031cc23c 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2TargetHandler.java @@ -268,7 +268,7 @@ private void onResetRead(Http2Reset http2Reset) { Http2MessageStateContext messageStateContext = outboundMsgHolder.getRequest().getHttp2MessageStateContext(); if (messageStateContext != null) { - messageStateContext.getSenderState().handleConnectionClose(outboundMsgHolder); + messageStateContext.getSenderState().handleRstStream(outboundMsgHolder); } } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/EntityBodyReceived.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/EntityBodyReceived.java index 2376d03276..932180bbc4 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/EntityBodyReceived.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/EntityBodyReceived.java @@ -118,4 +118,14 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { LOG.debug("Channel is closed"); } } + + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + LOG.warn("handleServerGoAway is not a dependant action of this state"); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + LOG.warn("handleRstStream is not a dependant action of this state"); + } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java index 2ee6af2ef8..cd8fd25f03 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java @@ -36,6 +36,8 @@ import org.slf4j.LoggerFactory; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_BODY; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.releaseContent; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.StateUtil.handleIncompleteInboundMessage; @@ -117,6 +119,18 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); } + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), + REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_BODY); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), + REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY); + } + private void onDataRead(Http2DataFrame http2DataFrame, OutboundMsgHolder outboundMsgHolder, boolean serverPush, Http2MessageStateContext http2MessageStateContext) { int streamId = http2DataFrame.getStreamId(); diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java index 0aa5bc74d3..aac3411a08 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java @@ -59,6 +59,8 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.INBOUND_RESPONSE; import static io.ballerina.stdlib.http.transport.contract.Constants.POOLED_BYTE_BUFFER_FACTORY; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.releaseContent; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.StateUtil.handleIncompleteInboundMessage; import static io.netty.handler.codec.http.HttpHeaderNames.TRAILER; @@ -141,6 +143,18 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_HEADERS); } + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), + REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), + REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS); + } + private void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame http2HeadersFrame, OutboundMsgHolder outboundMsgHolder, boolean serverPush, Http2MessageStateContext http2MessageStateContext) { diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java index 013644dc74..cf055f25c7 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java @@ -36,6 +36,8 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_BEFORE_INITIATING_INBOUND_RESPONSE; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.onPushPromiseRead; /** @@ -112,4 +114,16 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { new ServerConnectorException(REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); LOG.error(REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } + + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getResponseFuture().notifyHttpListener( + new ServerConnectorException(REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE)); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getResponseFuture().notifyHttpListener( + new ServerConnectorException(REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE)); + } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java index 2dbcf78b5b..6b155e8c05 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java @@ -103,4 +103,15 @@ void handleStreamTimeout(OutboundMsgHolder outboundMsgHolder, boolean serverPush * Handles the connection close event. */ void handleConnectionClose(OutboundMsgHolder outboundMsgHolder); + + + /** + * Handles the Stream close event due to receiving GoAway frame. + */ + void handleServerGoAway(OutboundMsgHolder outboundMsgHolder); + + /** + * Handles the Stream close event due to receiving RSTStream frame. + */ + void handleRstStream(OutboundMsgHolder outboundMsgHolder); } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java index 83fd4c30ba..7915fde92f 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java @@ -19,6 +19,7 @@ package io.ballerina.stdlib.http.transport.contractimpl.sender.states.http2; import io.ballerina.stdlib.http.transport.contract.exceptions.EndpointTimeOutException; +import io.ballerina.stdlib.http.transport.contract.exceptions.RequestCancelledException; import io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2MessageStateContext; import io.ballerina.stdlib.http.transport.contractimpl.sender.http2.Http2ClientChannel; import io.ballerina.stdlib.http.transport.contractimpl.sender.http2.Http2DataEventListener; @@ -44,6 +45,8 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_BODY; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_BODY; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_BODY; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.onPushPromiseRead; /** @@ -137,6 +140,24 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { HttpResponseStatus.GATEWAY_TIMEOUT.code())); } + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getRequest().setIoException( + new IOException(REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_BODY)); + outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( + REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_BODY, + HttpResponseStatus.BAD_GATEWAY.code())); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getRequest().setIoException( + new IOException(REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY)); + outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( + REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY, + HttpResponseStatus.BAD_GATEWAY.code())); + } + private void writeContent(ChannelHandlerContext ctx, HttpContent msg) throws Http2Exception { boolean release = true; try { diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java index 49ef3470d2..2ee7e6f34f 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java @@ -19,6 +19,7 @@ package io.ballerina.stdlib.http.transport.contractimpl.sender.states.http2; import io.ballerina.stdlib.http.transport.contract.exceptions.EndpointTimeOutException; +import io.ballerina.stdlib.http.transport.contract.exceptions.RequestCancelledException; import io.ballerina.stdlib.http.transport.contractimpl.common.Util; import io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2MessageStateContext; import io.ballerina.stdlib.http.transport.contractimpl.sender.http2.Http2ClientChannel; @@ -53,6 +54,8 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.INBOUND_RESPONSE_ALREADY_RECEIVED; import static io.ballerina.stdlib.http.transport.contract.Constants.PROTOCOL; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.initiateStream; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.writeHttp2Headers; @@ -140,6 +143,20 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { HttpResponseStatus.GATEWAY_TIMEOUT.code())); } + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( + REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS, + HttpResponseStatus.BAD_GATEWAY.code())); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( + REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS, + HttpResponseStatus.BAD_GATEWAY.code())); + } + private void writeHeaders(ChannelHandlerContext ctx, HttpContent msg) throws Http2Exception { // Initiate the stream boolean endStream = false; diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java index 789ea000d1..fb98b14b67 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java @@ -19,6 +19,8 @@ package io.ballerina.stdlib.http.transport.contractimpl.sender.states.http2; import io.ballerina.stdlib.http.transport.contract.exceptions.EndpointTimeOutException; +import io.ballerina.stdlib.http.transport.contract.exceptions.RequestCancelledException; +import io.ballerina.stdlib.http.transport.contract.exceptions.ServerConnectorException; import io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2MessageStateContext; import io.ballerina.stdlib.http.transport.contractimpl.sender.http2.Http2ClientChannel; import io.ballerina.stdlib.http.transport.contractimpl.sender.http2.Http2TargetHandler; @@ -35,8 +37,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_WHILE_SENDING_RST_STREAM; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_SENDING_RST_STREAM; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_SENDING_RST_STREAM; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_SENDING_RST_STREAM; /** * A state to reset the stream in the middle of communication. @@ -100,16 +104,29 @@ public void handleStreamTimeout(OutboundMsgHolder outboundMsgHolder, boolean ser int streamId) throws Http2Exception { if (!serverPush) { outboundMsgHolder.getResponseFuture().notifyHttpListener( - new EndpointTimeOutException(IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS, + new EndpointTimeOutException(IDLE_TIMEOUT_TRIGGERED_WHILE_SENDING_RST_STREAM, HttpResponseStatus.GATEWAY_TIMEOUT.code())); } } @Override public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { - outboundMsgHolder.getResponseFuture().notifyHttpListener(new EndpointTimeOutException( - REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS, - HttpResponseStatus.GATEWAY_TIMEOUT.code())); + outboundMsgHolder.getResponseFuture().notifyHttpListener(new ServerConnectorException( + REMOTE_SERVER_CLOSED_WHILE_SENDING_RST_STREAM)); + } + + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( + REMOTE_SERVER_SENT_GOAWAY_WHILE_SENDING_RST_STREAM, + HttpResponseStatus.BAD_GATEWAY.code())); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( + REMOTE_SERVER_SENT_RSTSTREAM_WHILE_SENDING_RST_STREAM, + HttpResponseStatus.BAD_GATEWAY.code())); } public void resetStream(ChannelHandlerContext ctx) { diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/WaitingFor100Continue.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/WaitingFor100Continue.java index 10715c495d..dbb26cfbb3 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/WaitingFor100Continue.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/WaitingFor100Continue.java @@ -40,6 +40,7 @@ import java.util.concurrent.ScheduledFuture; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.StateUtil.handleIncompleteInboundMessage; /** @@ -151,4 +152,16 @@ public void handleConnectionClose(OutboundMsgHolder outboundMsgHolder) { handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_HEADERS); } + + @Override + public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { + handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), + REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS); + } + + @Override + public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { + handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), + REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS); + } } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index 428b46f0a2..d1be07f7bd 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -91,7 +91,7 @@ private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { getErrorResponseMessage(msgListener4) : getResponseMessage(msgListener4); assertEqualsNoOrder(List.of(responseValOrError1, responseValOrError2, responseValOrError3, responseValOrError4), List.of("hello world3", "hello world5", "hello world7", - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE)); + Constants.REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE)); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java index 3639e07f07..06312df11c 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayWhileReceivingBodyScenarioTest.java @@ -70,7 +70,7 @@ private void testGoAwayWhenReceivingBodyForASingleStream() { LOGGER.error("Interrupted exception occurred"); } assertEquals(getDecoderErrorMessage(msgListener), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_BODY); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index b12286edbf..412179262f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -80,10 +80,10 @@ private void testRSTStreamFrameForMultipleStreams() { writeSemaphore.release(); readSemaphore.acquire(); assertEquals(getErrorResponseMessage(msgListener1), - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + Constants.REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE); assertEquals(getResponseMessage(msgListener2), "hello world5"); assertEquals(getDecoderErrorMessage(msgListener3), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); fail(); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index a3c0800263..0bc9bace3f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -68,7 +68,7 @@ private void testRSTStreamFrameForSingleStream() { LOGGER.error("Interrupted exception occurred"); } assertEquals(getErrorResponseMessage(msgListener), - Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + Constants.REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index 6d6d0befdc..bd4fe6f69d 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -70,7 +70,7 @@ private void testRSTStreamFrameWhenReadingBodyForSingleStream() { LOGGER.error("Interrupted exception occurred"); } assertEquals(getDecoderErrorMessage(msgListener), - Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY); + Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY); } From 964f553a71ff0a03e1625eb1cce9fda242d9c1f7 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 19 Jan 2024 09:33:28 +0530 Subject: [PATCH 36/46] Add suggestions from review comments --- .../http/transport/contract/Constants.java | 44 +++++++++---------- .../sender/http2/Http2ConnectionManager.java | 9 +--- .../states/http2/ReceivingEntityBody.java | 4 +- .../sender/states/http2/ReceivingHeaders.java | 4 +- .../sender/states/http2/RequestCompleted.java | 4 +- .../sender/states/http2/SenderState.java | 1 - .../states/http2/SendingEntityBody.java | 6 +-- .../sender/states/http2/SendingHeaders.java | 4 +- .../sender/states/http2/SendingRstFrame.java | 4 +- ...rRSTStreamFrameForMultipleStreamsTest.java | 4 +- ...rverRSTStreamFrameForSingleStreamTest.java | 2 +- ...ameWhenReadingBodyForSingleStreamTest.java | 2 +- 12 files changed, 41 insertions(+), 47 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java index 22452b994e..5f9f282e7a 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contract/Constants.java @@ -346,7 +346,7 @@ public final class Constants { public static final String IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS = "Idle timeout triggered while writing outbound request headers"; public static final String IDLE_TIMEOUT_TRIGGERED_WHILE_SENDING_RST_STREAM - = "Idle timeout triggered while sending RSTStream frame"; + = "Idle timeout triggered while sending RST_STREAM frame"; public static final String IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_BODY = "Idle timeout triggered while writing outbound request entity body"; public static final String IDLE_TIMEOUT_TRIGGERED_BEFORE_INITIATING_INBOUND_RESPONSE @@ -389,7 +389,7 @@ public final class Constants { public static final String REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS = "Remote host closed the connection while writing outbound request headers"; public static final String REMOTE_SERVER_CLOSED_WHILE_SENDING_RST_STREAM - = "Remote host closed the connection while sending RSTStream frame"; + = "Remote host closed the connection while sending RST_STREAM frame"; public static final String REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_BODY = "Remote host closed the connection while writing outbound request entity body"; public static final String REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE @@ -400,32 +400,32 @@ public final class Constants { = "Remote host closed the connection while reading inbound response body"; public static final String REMOTE_SERVER_CLOSED_BEFORE_READING_100_CONTINUE_RESPONSE = "Remote host closed the connection before reading 100 continue response"; - // Server GoAway error scenarios + // Server GOAWAY error scenarios public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS - = "Remote host sent GoAway while writing outbound request headers"; + = "Remote host sent GOAWAY while writing outbound request headers"; public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_SENDING_RST_STREAM - = "Remote host sent GoAway while sending RSTStream frame"; + = "Remote host sent GOAWAY while sending RST_STREAM frame"; public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_BODY - = "Remote host sent GoAway while writing outbound request entity body"; + = "Remote host sent GOAWAY while writing outbound request entity body"; public static final String REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE - = "Remote host sent GoAway before initiating inbound response"; + = "Remote host sent GOAWAY before initiating inbound response"; public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS - = "Remote host sent GoAway while reading inbound response headers"; + = "Remote host sent GOAWAY while reading inbound response headers"; public static final String REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_BODY - = "Remote host sent GoAway while reading inbound response body"; - // Server send RSTStream error scenarios - public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS - = "Remote host sent RSTStream while writing outbound request headers"; - public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_SENDING_RST_STREAM - = "Remote host sent RSTStream while sending RSTStream frame"; - public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY - = "Remote host sent RSTStream while writing outbound request entity body"; - public static final String REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE - = "Remote host sent RSTStream before initiating inbound response"; - public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS - = "Remote host sent RSTStream while reading inbound response headers"; - public static final String REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY - = "Remote host sent RSTStream while reading inbound response body"; + = "Remote host sent GOAWAY while reading inbound response body"; + // Server send RST_STREAM error scenarios + public static final String REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS + = "Remote host sent RST_STREAM while writing outbound request headers"; + public static final String REMOTE_SERVER_SENT_RST_STREAM_WHILE_SENDING_RST_STREAM + = "Remote host sent RST_STREAM while sending RST_STREAM frame"; + public static final String REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY + = "Remote host sent RST_STREAM while writing outbound request entity body"; + public static final String REMOTE_SERVER_SENT_RST_STREAM_BEFORE_INITIATING_INBOUND_RESPONSE + = "Remote host sent RST_STREAM before initiating inbound response"; + public static final String REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS + = "Remote host sent RST_STREAM while reading inbound response headers"; + public static final String REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_BODY + = "Remote host sent RST_STREAM while reading inbound response body"; public static final String REMOTE_CLIENT_TO_HOST_CONNECTION_CLOSED = "Connection between remote client and host is closed"; diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index 504d33265c..f920b3c4ed 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -121,14 +121,9 @@ private synchronized Http2ChannelPool.PerRouteConnectionPool createPerRouteConne */ public Http2ClientChannel fetchChannel(HttpRoute httpRoute) { Http2ChannelPool.PerRouteConnectionPool perRouteConnectionPool; - synchronized (this) { + synchronized (this) { perRouteConnectionPool = getOrCreatePerRoutePool(this.http2ChannelPool, generateKey(httpRoute)); - - Http2ClientChannel http2ClientChannel = null; - if (perRouteConnectionPool != null) { - http2ClientChannel = perRouteConnectionPool.fetchTargetChannel(); - } - return http2ClientChannel; + return perRouteConnectionPool != null ? perRouteConnectionPool.fetchTargetChannel() : null; } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java index cd8fd25f03..ac059d85c7 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingEntityBody.java @@ -37,7 +37,7 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_BODY; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_BODY; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.releaseContent; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.StateUtil.handleIncompleteInboundMessage; @@ -128,7 +128,7 @@ public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { @Override public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), - REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY); + REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_BODY); } private void onDataRead(Http2DataFrame http2DataFrame, OutboundMsgHolder outboundMsgHolder, boolean serverPush, diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java index aac3411a08..6301d9b475 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/ReceivingHeaders.java @@ -60,7 +60,7 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.POOLED_BYTE_BUFFER_FACTORY; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_HEADERS; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_READING_INBOUND_RESPONSE_HEADERS; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.releaseContent; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.StateUtil.handleIncompleteInboundMessage; import static io.netty.handler.codec.http.HttpHeaderNames.TRAILER; @@ -152,7 +152,7 @@ public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { @Override public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { handleIncompleteInboundMessage(outboundMsgHolder.getResponse(), - REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS); + REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_HEADERS); } private void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame http2HeadersFrame, diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java index cf055f25c7..af2488572b 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/RequestCompleted.java @@ -37,7 +37,7 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_BEFORE_INITIATING_INBOUND_RESPONSE; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RST_STREAM_BEFORE_INITIATING_INBOUND_RESPONSE; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.onPushPromiseRead; /** @@ -124,6 +124,6 @@ public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { @Override public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { outboundMsgHolder.getResponseFuture().notifyHttpListener( - new ServerConnectorException(REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE)); + new ServerConnectorException(REMOTE_SERVER_SENT_RST_STREAM_BEFORE_INITIATING_INBOUND_RESPONSE)); } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java index 6b155e8c05..2a18ce8390 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SenderState.java @@ -104,7 +104,6 @@ void handleStreamTimeout(OutboundMsgHolder outboundMsgHolder, boolean serverPush */ void handleConnectionClose(OutboundMsgHolder outboundMsgHolder); - /** * Handles the Stream close event due to receiving GoAway frame. */ diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java index 7915fde92f..47de712cde 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingEntityBody.java @@ -46,7 +46,7 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_WHILE_WRITING_OUTBOUND_REQUEST_BODY; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_BODY; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_BODY; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.onPushPromiseRead; /** @@ -152,9 +152,9 @@ public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { @Override public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { outboundMsgHolder.getRequest().setIoException( - new IOException(REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY)); + new IOException(REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY)); outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( - REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY, + REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_BODY, HttpResponseStatus.BAD_GATEWAY.code())); } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java index 2ee7e6f34f..7298c3e1db 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingHeaders.java @@ -55,7 +55,7 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.PROTOCOL; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.initiateStream; import static io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2StateUtil.writeHttp2Headers; @@ -153,7 +153,7 @@ public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { @Override public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( - REMOTE_SERVER_SENT_RSTSTREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS, + REMOTE_SERVER_SENT_RST_STREAM_WHILE_WRITING_OUTBOUND_REQUEST_HEADERS, HttpResponseStatus.BAD_GATEWAY.code())); } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java index fb98b14b67..1808b0ab46 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/states/http2/SendingRstFrame.java @@ -40,7 +40,7 @@ import static io.ballerina.stdlib.http.transport.contract.Constants.IDLE_TIMEOUT_TRIGGERED_WHILE_SENDING_RST_STREAM; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_SENDING_RST_STREAM; import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_GOAWAY_WHILE_SENDING_RST_STREAM; -import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_SENDING_RST_STREAM; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_SENDING_RST_STREAM; /** * A state to reset the stream in the middle of communication. @@ -125,7 +125,7 @@ public void handleServerGoAway(OutboundMsgHolder outboundMsgHolder) { @Override public void handleRstStream(OutboundMsgHolder outboundMsgHolder) { outboundMsgHolder.getResponseFuture().notifyHttpListener(new RequestCancelledException( - REMOTE_SERVER_SENT_RSTSTREAM_WHILE_SENDING_RST_STREAM, + REMOTE_SERVER_SENT_RST_STREAM_WHILE_SENDING_RST_STREAM, HttpResponseStatus.BAD_GATEWAY.code())); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index 412179262f..a6c6eb636f 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -80,10 +80,10 @@ private void testRSTStreamFrameForMultipleStreams() { writeSemaphore.release(); readSemaphore.acquire(); assertEquals(getErrorResponseMessage(msgListener1), - Constants.REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE); + Constants.REMOTE_SERVER_SENT_RST_STREAM_BEFORE_INITIATING_INBOUND_RESPONSE); assertEquals(getResponseMessage(msgListener2), "hello world5"); assertEquals(getDecoderErrorMessage(msgListener3), - Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY); + Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_BODY); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); fail(); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java index 0bc9bace3f..785cbbf535 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForSingleStreamTest.java @@ -68,7 +68,7 @@ private void testRSTStreamFrameForSingleStream() { LOGGER.error("Interrupted exception occurred"); } assertEquals(getErrorResponseMessage(msgListener), - Constants.REMOTE_SERVER_SENT_RSTSTREAM_BEFORE_INITIATING_INBOUND_RESPONSE); + Constants.REMOTE_SERVER_SENT_RST_STREAM_BEFORE_INITIATING_INBOUND_RESPONSE); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java index bd4fe6f69d..b57daff8e9 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameWhenReadingBodyForSingleStreamTest.java @@ -70,7 +70,7 @@ private void testRSTStreamFrameWhenReadingBodyForSingleStream() { LOGGER.error("Interrupted exception occurred"); } assertEquals(getDecoderErrorMessage(msgListener), - Constants.REMOTE_SERVER_SENT_RSTSTREAM_WHILE_READING_INBOUND_RESPONSE_BODY); + Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_BODY); } From 225683778398be92002fc4b9e51465c6d8d56377 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 19 Jan 2024 09:46:32 +0530 Subject: [PATCH 37/46] Update class name --- .../contractimpl/sender/HttpClientChannelInitializer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java index 9e5fbd5b19..7f4b0c6b48 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/HttpClientChannelInitializer.java @@ -220,7 +220,7 @@ private void configureSslForHttp2(SocketChannel ch, ChannelPipeline clientPipeli } } clientPipeline.addLast( - new Http2PipelineConfiguratorAfterALPNNegotationForClient(targetHandler, connectionAvailabilityFuture)); + new ALPNClientHandler(targetHandler, connectionAvailabilityFuture)); clientPipeline .addLast(Constants.HTTP2_EXCEPTION_HANDLER, new Http2ExceptionHandler(http2ConnectionHandler)); } @@ -330,13 +330,13 @@ public void setHttp2ClientChannel(Http2ClientChannel http2ClientChannel) { /** * A handler to create the pipeline based on the ALPN negotiated protocol. */ - class Http2PipelineConfiguratorAfterALPNNegotationForClient extends ApplicationProtocolNegotiationHandler { + class ALPNClientHandler extends ApplicationProtocolNegotiationHandler { private TargetHandler targetHandler; private ConnectionAvailabilityFuture connectionAvailabilityFuture; - public Http2PipelineConfiguratorAfterALPNNegotationForClient(TargetHandler targetHandler, - ConnectionAvailabilityFuture connectionAvailabilityFuture) { + public ALPNClientHandler(TargetHandler targetHandler, + ConnectionAvailabilityFuture connectionAvailabilityFuture) { super(ApplicationProtocolNames.HTTP_1_1); this.targetHandler = targetHandler; this.connectionAvailabilityFuture = connectionAvailabilityFuture; From e1755c95fa8cccd674c6693ccb3410d0e24ca04c Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 19 Jan 2024 13:50:49 +0530 Subject: [PATCH 38/46] Add test case for latch hang --- ...verAbruptClosureInUpgradeScenarioTest.java | 157 ++++++++++++++++++ native/src/test/resources/testng.xml | 1 + 2 files changed, 158 insertions(+) create mode 100644 native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java new file mode 100644 index 0000000000..419ffbee81 --- /dev/null +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.stdlib.http.transport.http2.frameleveltests; + +import io.ballerina.stdlib.http.transport.contract.Constants; +import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; +import io.ballerina.stdlib.http.transport.contract.HttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; +import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; +import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; +import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; +import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; +import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; +import io.ballerina.stdlib.http.transport.util.TestUtil; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static org.testng.Assert.assertEqualsNoOrder; +import static org.testng.Assert.fail; + +/** + * This contains a test case where the tcp server sends a GoAway and the connection gets timed out from client side. + */ +public class Http2ServerAbruptClosureInUpgradeScenarioTest { + + private static final Logger LOGGER = + LoggerFactory.getLogger(Http2ServerAbruptClosureInUpgradeScenarioTest.class); + private HttpClientConnector h2ClientWithPriorKnowledge; + private ServerSocket serverSocket; + private int numOfConnections = 0; + + @BeforeClass + public void setup() throws InterruptedException { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + h2ClientWithPriorKnowledge = setupHttp2PriorKnowledgeClient(); + } + + public HttpClientConnector setupHttp2PriorKnowledgeClient() { + HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); + PoolConfiguration poolConfiguration = new PoolConfiguration(); + poolConfiguration.setMinEvictableIdleTime(5000); + poolConfiguration.setTimeBetweenEvictionRuns(1000); + TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); + SenderConfiguration senderConfiguration = new SenderConfiguration(); + senderConfiguration.setPoolConfiguration(poolConfiguration); + senderConfiguration.setScheme(Constants.HTTP_SCHEME); + senderConfiguration.setHttpVersion(Constants.HTTP_2_0); + senderConfiguration.setForceHttp2(false); + return connectorFactory.createHttpClientConnector( + HttpConnectorUtil.getTransportProperties(transportsConfiguration), senderConfiguration); + } + + @Test + private void testServerAbruptClosureInUpgradeScenario() { + try { + CountDownLatch latch1 = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); + latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + + CountDownLatch latch2 = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch2, h2ClientWithPriorKnowledge); + latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + + HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); + HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); + + Object responseVal1 = response1.getHttpContent().content().toString(CharsetUtil.UTF_8); + Object responseVal2 = response2.getHttpContent().content().toString(CharsetUtil.UTF_8); + + assertEqualsNoOrder(List.of(responseVal1, responseVal2), + List.of("hello world3", "hello world4")); + } catch (InterruptedException e) { + LOGGER.error("Interrupted exception occurred"); + fail(); + } + } + + private void runTcpServer(int port) { + new Thread(() -> { + try { + serverSocket = new ServerSocket(port); + LOGGER.info("HTTP/2 TCP Server listening on port " + port); + while (numOfConnections < 2) { + Socket clientSocket = serverSocket.accept(); + LOGGER.info("Accepted connection from: " + clientSocket.getInetAddress()); + try (OutputStream outputStream = clientSocket.getOutputStream(); + InputStream inputStream = clientSocket.getInputStream()) { + readSocketAndExit(inputStream); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + } + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }).start(); + } + + private void readSocketAndExit(InputStream inputStream) throws IOException { + byte[] buffer = new byte[4096]; + int bytesRead = 0; + try { + Thread.sleep(1000); + bytesRead = inputStream.read(buffer); + } catch (Exception e) { + e.printStackTrace(); + } + String data = new String(buffer, 0, bytesRead); + System.out.println("Received data: " + data); + } + + @AfterClass + public void cleanUp() throws IOException { + h2ClientWithPriorKnowledge.close(); + serverSocket.close(); + } +} diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index 8cd178175e..7944f41b74 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -163,6 +163,7 @@ + From 0f8cb19cf5594cbc60a8ee504c8f6b0dad928556 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 19 Jan 2024 14:00:51 +0530 Subject: [PATCH 39/46] Format test case --- ...verAbruptClosureInUpgradeScenarioTest.java | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java index 419ffbee81..3d73b2d0c2 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ServerAbruptClosureInUpgradeScenarioTest.java @@ -24,12 +24,9 @@ import io.ballerina.stdlib.http.transport.contract.config.SenderConfiguration; import io.ballerina.stdlib.http.transport.contract.config.TransportsConfiguration; import io.ballerina.stdlib.http.transport.contractimpl.DefaultHttpWsConnectorFactory; -import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; -import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import io.ballerina.stdlib.http.transport.message.HttpConnectorUtil; import io.ballerina.stdlib.http.transport.util.DefaultHttpConnectorListener; import io.ballerina.stdlib.http.transport.util.TestUtil; -import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterClass; @@ -41,22 +38,13 @@ import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_05; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; -import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; -import static org.testng.Assert.assertEqualsNoOrder; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; import static org.testng.Assert.fail; +import static org.testng.AssertJUnit.assertEquals; /** * This contains a test case where the tcp server sends a GoAway and the connection gets timed out from client side. @@ -65,24 +53,20 @@ public class Http2ServerAbruptClosureInUpgradeScenarioTest { private static final Logger LOGGER = LoggerFactory.getLogger(Http2ServerAbruptClosureInUpgradeScenarioTest.class); - private HttpClientConnector h2ClientWithPriorKnowledge; + private HttpClientConnector h2ClientWithUpgrade; private ServerSocket serverSocket; private int numOfConnections = 0; @BeforeClass public void setup() throws InterruptedException { runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = setupHttp2PriorKnowledgeClient(); + h2ClientWithUpgrade = setupHttp2UpgradeClient(); } - public HttpClientConnector setupHttp2PriorKnowledgeClient() { + public HttpClientConnector setupHttp2UpgradeClient() { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); - PoolConfiguration poolConfiguration = new PoolConfiguration(); - poolConfiguration.setMinEvictableIdleTime(5000); - poolConfiguration.setTimeBetweenEvictionRuns(1000); TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); SenderConfiguration senderConfiguration = new SenderConfiguration(); - senderConfiguration.setPoolConfiguration(poolConfiguration); senderConfiguration.setScheme(Constants.HTTP_SCHEME); senderConfiguration.setHttpVersion(Constants.HTTP_2_0); senderConfiguration.setForceHttp2(false); @@ -94,21 +78,17 @@ public HttpClientConnector setupHttp2PriorKnowledgeClient() { private void testServerAbruptClosureInUpgradeScenario() { try { CountDownLatch latch1 = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithUpgrade); latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); CountDownLatch latch2 = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch2, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch2, h2ClientWithUpgrade); latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); - HttpCarbonMessage response2 = msgListener2.getHttpResponseMessage(); - - Object responseVal1 = response1.getHttpContent().content().toString(CharsetUtil.UTF_8); - Object responseVal2 = response2.getHttpContent().content().toString(CharsetUtil.UTF_8); - - assertEqualsNoOrder(List.of(responseVal1, responseVal2), - List.of("hello world3", "hello world4")); + assertEquals(getErrorResponseMessage(msgListener1), + REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); + assertEquals(getErrorResponseMessage(msgListener2), + REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); fail(); @@ -126,6 +106,7 @@ private void runTcpServer(int port) { try (OutputStream outputStream = clientSocket.getOutputStream(); InputStream inputStream = clientSocket.getInputStream()) { readSocketAndExit(inputStream); + numOfConnections += 1; } catch (Exception e) { LOGGER.error(e.getMessage()); } @@ -137,21 +118,22 @@ private void runTcpServer(int port) { } private void readSocketAndExit(InputStream inputStream) throws IOException { + // This will just read the socket input content and exit the socket without sending any response + // which will trigger the channel inactive in the client side byte[] buffer = new byte[4096]; int bytesRead = 0; try { - Thread.sleep(1000); bytesRead = inputStream.read(buffer); - } catch (Exception e) { - e.printStackTrace(); + } catch (Exception exception) { + LOGGER.error(exception.getMessage()); } String data = new String(buffer, 0, bytesRead); - System.out.println("Received data: " + data); + LOGGER.info("Received upgrade requesst: " + data); } @AfterClass public void cleanUp() throws IOException { - h2ClientWithPriorKnowledge.close(); + h2ClientWithUpgrade.close(); serverSocket.close(); } } From 78bba5d72fa61faeefbcf89e3db7ce896c67c6a5 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 19 Jan 2024 23:41:16 +0530 Subject: [PATCH 40/46] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 321c5e85c0..2798f6a61b 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -25,7 +25,7 @@ modules = [ [[package]] org = "ballerina" name = "cache" -version = "3.7.0" +version = "3.7.1" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "jballerina.java"}, @@ -283,7 +283,7 @@ modules = [ [[package]] org = "ballerina" name = "observe" -version = "1.2.0" +version = "1.2.2" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From 00f8ff3d7283b5c08666b592ed3166e3f393dd9d Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Tue, 23 Jan 2024 09:25:57 +0530 Subject: [PATCH 41/46] Add check for channel removal --- .../contractimpl/sender/http2/Http2ConnectionManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index f920b3c4ed..bd405fcbcf 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -22,6 +22,8 @@ import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; import io.netty.channel.DefaultEventLoop; import io.netty.util.concurrent.DefaultPromise; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Timer; import java.util.TimerTask; @@ -165,13 +167,17 @@ void markClientChannelAsStale(HttpRoute httpRoute, Http2ClientChannel http2Clien private void initiateConnectionEvictionTask() { Timer timer = new Timer(true); TimerTask timerTask = new TimerTask() { + Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void run() { http2StaleClientChannels.forEach(http2ClientChannel -> { if ((System.currentTimeMillis() - http2ClientChannel.getTimeSinceMarkedAsStale()) > poolConfiguration.getMinEvictableIdleTime() && !http2ClientChannel.hasInFlightMessages()) { - http2StaleClientChannels.remove(http2ClientChannel); + boolean result = http2StaleClientChannels.remove(http2ClientChannel); + if (!result) { + logger.warn("Specified channel does not exist in the stale list."); + } http2ClientChannel.getConnection() .close(new DefaultPromise(new DefaultEventLoop())); } From 087805a813a0c5978833dc1e637b7e50a9133e93 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Wed, 24 Jan 2024 11:23:51 +0530 Subject: [PATCH 42/46] Update http2 connection evict logic --- ballerina/http_client_connection_pool.bal | 7 ++ .../stdlib/http/api/HttpConstants.java | 4 + .../ballerina/stdlib/http/api/HttpUtil.java | 17 +++- .../channel/pool/PoolConfiguration.java | 18 +++++ .../sender/http2/Http2ClientChannel.java | 4 + .../sender/http2/Http2ConnectionManager.java | 35 ++++++--- .../frameleveltests/FrameLevelTestUtils.java | 6 +- ...ctionAfterTcpServerGoAwayScenarioTest.java | 77 ++++++++++++++----- ...erverGoAwayMultipleStreamScenarioTest.java | 7 +- ...pServerGoAwaySingleStreamScenarioTest.java | 3 +- ...rRSTStreamFrameForMultipleStreamsTest.java | 3 +- ...erSendGoAwayForAllStreamsScenarioTest.java | 14 ++-- .../Http2TcpServerSuccessScenarioTest.java | 3 +- 13 files changed, 155 insertions(+), 43 deletions(-) diff --git a/ballerina/http_client_connection_pool.bal b/ballerina/http_client_connection_pool.bal index 30dd981cba..fd4fbcbb45 100644 --- a/ballerina/http_client_connection_pool.bal +++ b/ballerina/http_client_connection_pool.bal @@ -29,6 +29,11 @@ configurable int maxActiveStreamsPerConnection = 100; # + maxActiveStreamsPerConnection - Maximum active streams per connection. This only applies to HTTP/2. Default value is 100 # + minEvictableIdleTime - Minimum evictable time for an idle connection in seconds. Default value is 5 minutes # + timeBetweenEvictionRuns - Time between eviction runs in seconds. Default value is 30 seconds +# + minIdleTimeInStaleState - Minimum time in seconds for a connection to be kept open which has received a GOAWAY. +# This only applies for HTTP/2. Default value is 5 minutes. If the value is set to -1, +# the connection will not be closed until all existing streams are completed +# + timeBetweenStaleCheck - Time between the connection stale check run in seconds. This only applies for HTTP/2. +# Default value is 30 seconds public type PoolConfiguration record {| int maxActiveConnections = maxActiveConnections; int maxIdleConnections = maxIdleConnections; @@ -36,6 +41,8 @@ public type PoolConfiguration record {| int maxActiveStreamsPerConnection = maxActiveStreamsPerConnection; decimal minEvictableIdleTime = 300; decimal timeBetweenEvictionRuns = 30; + decimal minIdleTimeInStaleState = 300; + decimal timeBetweenStaleCheck = 30; |}; //This is a hack to get the global map initialized, without involving locking. diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java index 286e621115..766ee36ffa 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java @@ -510,6 +510,10 @@ public final class HttpConstants { "minEvictableIdleTime"); public static final BString CONNECTION_POOLING_TIME_BETWEEN_EVICTION_RUNS = StringUtils.fromString( "timeBetweenEvictionRuns"); + public static final BString CONNECTION_POOLING_IDLE_TIME_STALE_STATE = StringUtils.fromString( + "minIdleTimeInStaleState"); + public static final BString CONNECTION_POOLING_TIME_BETWEEN_STALE_CHECK_RUNS = StringUtils.fromString( + "timeBetweenStaleCheck"); public static final String HTTP_CLIENT_CONNECTION_POOL = "PoolConfiguration"; public static final String CONNECTION_MANAGER = "ConnectionManager"; public static final int POOL_CONFIG_INDEX = 1; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java index 6d8b96e59e..f0e0fb3933 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java @@ -1359,8 +1359,21 @@ public static void populatePoolingConfig(BMap poolRecord, PoolConfiguration pool double timeBetweenEvictionRuns = ((BDecimal) poolRecord.get(HttpConstants.CONNECTION_POOLING_TIME_BETWEEN_EVICTION_RUNS)).floatValue(); - poolConfiguration.setTimeBetweenEvictionRuns( - timeBetweenEvictionRuns < 0 ? 0 : (long) timeBetweenEvictionRuns * 1000); + if (timeBetweenEvictionRuns > 0) { + poolConfiguration.setTimeBetweenEvictionRuns((long) timeBetweenEvictionRuns * 1000); + } + + double minIdleTimeInStaleState = + ((BDecimal) poolRecord.get(HttpConstants.CONNECTION_POOLING_IDLE_TIME_STALE_STATE)).floatValue(); + poolConfiguration.setMinIdleTimeInStaleState(minIdleTimeInStaleState < -1 ? -1 : + (long) minEvictableIdleTime * 1000); + + double timeBetweenStaleCheck = + ((BDecimal) poolRecord.get(HttpConstants.CONNECTION_POOLING_TIME_BETWEEN_STALE_CHECK_RUNS)) + .floatValue(); + if (timeBetweenStaleCheck > 0) { + poolConfiguration.setTimeBetweenStaleCheck((long) timeBetweenStaleCheck * 1000); + } } private static int validateConfig(long value, String configName) { diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java index 9ab58dc6d0..06db1077a3 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java @@ -35,6 +35,8 @@ public class PoolConfiguration { private int eventGroupExecutorThreads = 15; private long maxWaitTime = 60000L; private int http2MaxActiveStreamsPerConnection = Integer.MAX_VALUE; + private long minIdleTimeInStaleState = 300000; + private long timeBetweenStaleCheck = 30000; public PoolConfiguration() { } @@ -142,4 +144,20 @@ public int getHttp2MaxActiveStreamsPerConnection() { public void setHttp2MaxActiveStreamsPerConnection(int http2MaxActiveStreamsPerConnection) { this.http2MaxActiveStreamsPerConnection = http2MaxActiveStreamsPerConnection; } + + public long getMinIdleTimeInStaleState() { + return minIdleTimeInStaleState; + } + + public void setMinIdleTimeInStaleState(long minIdleTimeInStaleState) { + this.minIdleTimeInStaleState = minIdleTimeInStaleState; + } + + public long getTimeBetweenStaleCheck() { + return timeBetweenStaleCheck; + } + + public void setTimeBetweenStaleCheck(long timeBetweenStaleCheck) { + this.timeBetweenStaleCheck = timeBetweenStaleCheck; + } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java index 47d54ea6ca..ee3d0b0dbe 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ClientChannel.java @@ -274,6 +274,10 @@ private void handleConnectionClose() { } } + ConcurrentHashMap getInFlightMessages() { + return inFlightMessages; + } + /** * Listener which listen to the stream closure event. */ diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index bd405fcbcf..2631a3b5c5 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -19,6 +19,7 @@ package io.ballerina.stdlib.http.transport.contractimpl.sender.http2; import io.ballerina.stdlib.http.transport.contractimpl.common.HttpRoute; +import io.ballerina.stdlib.http.transport.contractimpl.common.states.Http2MessageStateContext; import io.ballerina.stdlib.http.transport.contractimpl.sender.channel.pool.PoolConfiguration; import io.netty.channel.DefaultEventLoop; import io.netty.util.concurrent.DefaultPromise; @@ -171,21 +172,35 @@ private void initiateConnectionEvictionTask() { @Override public void run() { http2StaleClientChannels.forEach(http2ClientChannel -> { - if ((System.currentTimeMillis() - http2ClientChannel.getTimeSinceMarkedAsStale()) > - poolConfiguration.getMinEvictableIdleTime() - && !http2ClientChannel.hasInFlightMessages()) { - boolean result = http2StaleClientChannels.remove(http2ClientChannel); - if (!result) { - logger.warn("Specified channel does not exist in the stale list."); + if (poolConfiguration.getMinIdleTimeInStaleState() == -1) { + if (!http2ClientChannel.hasInFlightMessages()) { + closeChannelAndEvict(http2ClientChannel); } - http2ClientChannel.getConnection() - .close(new DefaultPromise(new DefaultEventLoop())); + } else if ((System.currentTimeMillis() - http2ClientChannel.getTimeSinceMarkedAsStale()) > + poolConfiguration.getMinIdleTimeInStaleState()) { + http2ClientChannel.getInFlightMessages().forEach((streamId, outboundMsgHolder) -> { + Http2MessageStateContext messageStateContext = + outboundMsgHolder.getRequest().getHttp2MessageStateContext(); + if (messageStateContext != null) { + messageStateContext.getSenderState().handleConnectionClose(outboundMsgHolder); + } + }); + closeChannelAndEvict(http2ClientChannel); } }); } + + public void closeChannelAndEvict(Http2ClientChannel http2ClientChannel) { + boolean result = http2StaleClientChannels.remove(http2ClientChannel); + if (!result) { + logger.warn("Specified channel does not exist in the stale list."); + } + http2ClientChannel.getConnection() + .close(new DefaultPromise(new DefaultEventLoop())); + } }; - timer.schedule(timerTask, poolConfiguration.getTimeBetweenEvictionRuns(), - poolConfiguration.getTimeBetweenEvictionRuns()); + timer.schedule(timerTask, poolConfiguration.getTimeBetweenStaleCheck(), + poolConfiguration.getTimeBetweenStaleCheck()); } private Http2ChannelPool.PerRouteConnectionPool fetchPerRoutePool(HttpRoute httpRoute) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java index 8034e9ea97..a3c9dfa072 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/FrameLevelTestUtils.java @@ -64,12 +64,14 @@ public class FrameLevelTestUtils { 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0b}; public static final byte[] GO_AWAY_FRAME_MAX_STREAM_07 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0b}; - public static final byte[] GO_AWAY_FRAME_MAX_STREAM_09 = new byte[]{0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b}; public static final byte[] RST_STREAM_FRAME_STREAM_03 = new byte[]{0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02}; public static final byte[] RST_STREAM_FRAME_STREAM_07 = new byte[]{0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02}; + public static final String DATA_VALUE_HELLO_WORLD_03 = "hello world3"; + public static final String DATA_VALUE_HELLO_WORLD_04 = "hello world4"; + public static final String DATA_VALUE_HELLO_WORLD_05 = "hello world5"; + public static final String DATA_VALUE_HELLO_WORLD_07 = "hello world7"; public static HttpClientConnector setupHttp2PriorKnowledgeClient() { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java index 0387057a65..2bf163c930 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java @@ -33,7 +33,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.IOException; @@ -44,10 +43,15 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE; +import static io.ballerina.stdlib.http.transport.contract.Constants.REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests .FrameLevelTestUtils.DATA_FRAME_STREAM_03_DIFFERENT_DATA; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_04; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_05; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_05; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; @@ -55,6 +59,10 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME_WITH_ACK; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SLEEP_TIME; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getDecoderErrorMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getErrorResponseMessage; +import static io.ballerina.stdlib.http.transport.util.TestUtil.getResponseMessage; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEqualsNoOrder; import static org.testng.Assert.fail; @@ -69,17 +77,12 @@ public class Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest { private ServerSocket serverSocket; private int numOfConnections = 0; - @BeforeClass - public void setup() throws InterruptedException { - runTcpServer(TestUtil.HTTP_SERVER_PORT); - h2ClientWithPriorKnowledge = setupHttp2PriorKnowledgeClient(); - } - - public HttpClientConnector setupHttp2PriorKnowledgeClient() { + public HttpClientConnector setupHttp2PriorKnowledgeClient(long minIdleTimeInStaleState, + long timeBetweenStaleCheck) { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); PoolConfiguration poolConfiguration = new PoolConfiguration(); - poolConfiguration.setMinEvictableIdleTime(5000); - poolConfiguration.setTimeBetweenEvictionRuns(1000); + poolConfiguration.setMinIdleTimeInStaleState(minIdleTimeInStaleState); + poolConfiguration.setTimeBetweenStaleCheck(timeBetweenStaleCheck); TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); SenderConfiguration senderConfiguration = new SenderConfiguration(); senderConfiguration.setPoolConfiguration(poolConfiguration); @@ -91,15 +94,18 @@ public HttpClientConnector setupHttp2PriorKnowledgeClient() { } @Test - private void testConnectionEvictionAfterServerGoAwayScenario() { + private void testConnectionEvictionAfterAllStreamsAreClosedScenario() { try { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + // Setting to -1 will make the runner to wait until all pending streams are completed + h2ClientWithPriorKnowledge = setupHttp2PriorKnowledgeClient(-1, 1000); CountDownLatch latch1 = new CountDownLatch(2); DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); CountDownLatch latch2 = new CountDownLatch(1); - DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(latch2, h2ClientWithPriorKnowledge); latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); HttpCarbonMessage response1 = msgListener1.getHttpResponseMessage(); @@ -110,15 +116,50 @@ private void testConnectionEvictionAfterServerGoAwayScenario() { Object responseVal2 = response2.getHttpContent().content().toString(CharsetUtil.UTF_8); Object responseVal3 = response3.getHttpContent().content().toString(CharsetUtil.UTF_8); - assertEqualsNoOrder(List.of(responseVal1, responseVal2, responseVal3), - List.of("hello world3", "hello world4", "hello world5")); - } catch (InterruptedException e) { - LOGGER.error("Interrupted exception occurred"); + assertEqualsNoOrder(List.of(responseVal1, responseVal2), List.of(DATA_VALUE_HELLO_WORLD_03, + DATA_VALUE_HELLO_WORLD_05)); + assertEquals(responseVal3, DATA_VALUE_HELLO_WORLD_04); + } catch (InterruptedException | IOException e) { + LOGGER.error("Exception occurred"); fail(); } } - private void runTcpServer(int port) { + @Test + private void testConnectionEvictionBeforeAllStreamsAreClosedScenario() { + try { + runTcpServer(TestUtil.HTTP_SERVER_PORT); + // Setting to -1 will make the runner to wait until all pending streams are completed + h2ClientWithPriorKnowledge = setupHttp2PriorKnowledgeClient(5000, 1000); + CountDownLatch latch1 = new CountDownLatch(2); + DefaultHttpConnectorListener msgListener1 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); + DefaultHttpConnectorListener msgListener2 = TestUtil.sendRequestAsync(latch1, h2ClientWithPriorKnowledge); + latch1.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + + CountDownLatch latch2 = new CountDownLatch(1); + DefaultHttpConnectorListener msgListener3 = TestUtil.sendRequestAsync(latch2, h2ClientWithPriorKnowledge); + latch2.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); + + String errorMsg1 = msgListener1.getHttpErrorMessage() != null ? getErrorResponseMessage(msgListener1) : + getDecoderErrorMessage(msgListener1); + String errorMsg2 = msgListener2.getHttpErrorMessage() != null ? getErrorResponseMessage(msgListener2) : + getDecoderErrorMessage(msgListener2); + + assertEqualsNoOrder(List.of(errorMsg1, errorMsg2), + List.of(REMOTE_SERVER_CLOSED_BEFORE_INITIATING_INBOUND_RESPONSE, + REMOTE_SERVER_CLOSED_WHILE_READING_INBOUND_RESPONSE_BODY)); + assertEquals(getResponseMessage(msgListener3), DATA_VALUE_HELLO_WORLD_04); + } catch (InterruptedException | IOException e) { + LOGGER.error("Exception occurred"); + fail(); + } + } + + private void runTcpServer(int port) throws IOException { + if (serverSocket != null) { + serverSocket.close(); + } + numOfConnections = 0; new Thread(() -> { try { serverSocket = new ServerSocket(port); @@ -129,12 +170,12 @@ private void runTcpServer(int port) { try (OutputStream outputStream = clientSocket.getOutputStream()) { if (numOfConnections == 0) { sendGoAwayForASingleStream(outputStream); - numOfConnections += 1; } else { // If the connection successfully closed, a new socket connection // will be opened and it will come here sendSuccessfulRequest(outputStream); } + numOfConnections += 1; } catch (Exception e) { LOGGER.error(e.getMessage()); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java index d1be07f7bd..47f6252a3b 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwayMultipleStreamScenarioTest.java @@ -40,6 +40,9 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_07; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_09; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_07; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_07; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; @@ -90,8 +93,8 @@ private void testGoAwayWhenReceivingHeadersInAMultipleStreamScenario() { Object responseValOrError4 = msgListener4.getHttpResponseMessage() == null ? getErrorResponseMessage(msgListener4) : getResponseMessage(msgListener4); assertEqualsNoOrder(List.of(responseValOrError1, responseValOrError2, responseValOrError3, - responseValOrError4), List.of("hello world3", "hello world5", "hello world7", - Constants.REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE)); + responseValOrError4), List.of(DATA_VALUE_HELLO_WORLD_03, DATA_VALUE_HELLO_WORLD_05, + DATA_VALUE_HELLO_WORLD_07, Constants.REMOTE_SERVER_SENT_GOAWAY_BEFORE_INITIATING_INBOUND_RESPONSE)); } private void runTcpServer(int port) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java index 14c9e04c77..501b6e13fd 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerGoAwaySingleStreamScenarioTest.java @@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; @@ -68,7 +69,7 @@ private void testGoAwayWhenReceivingHeadersForASingleStream() { CountDownLatch latch = new CountDownLatch(1); DefaultHttpConnectorListener msgListener = TestUtil.sendRequestAsync(latch, h2ClientWithPriorKnowledge); latch.await(TestUtil.HTTP2_RESPONSE_TIME_OUT, TimeUnit.SECONDS); - assertEquals(getResponseMessage(msgListener), "hello world3"); + assertEquals(getResponseMessage(msgListener), DATA_VALUE_HELLO_WORLD_03); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java index a6c6eb636f..224d58c505 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerRSTStreamFrameForMultipleStreamsTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.Semaphore; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_05; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_05; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_05; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_07; @@ -81,7 +82,7 @@ private void testRSTStreamFrameForMultipleStreams() { readSemaphore.acquire(); assertEquals(getErrorResponseMessage(msgListener1), Constants.REMOTE_SERVER_SENT_RST_STREAM_BEFORE_INITIATING_INBOUND_RESPONSE); - assertEquals(getResponseMessage(msgListener2), "hello world5"); + assertEquals(getResponseMessage(msgListener2), DATA_VALUE_HELLO_WORLD_05); assertEquals(getDecoderErrorMessage(msgListener3), Constants.REMOTE_SERVER_SENT_RST_STREAM_WHILE_READING_INBOUND_RESPONSE_BODY); } catch (InterruptedException e) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java index 71834a372f..d6f4b07c24 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSendGoAwayForAllStreamsScenarioTest.java @@ -36,6 +36,8 @@ import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils .DATA_FRAME_STREAM_03_DIFFERENT_DATA; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_04; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.GO_AWAY_FRAME_MAX_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; @@ -80,12 +82,12 @@ private void testGoAwayForAllStreamsScenario() { semaphore.acquire(); DefaultHttpConnectorListener msgListener6 = TestUtil.sendRequestAsync(null, h2ClientWithPriorKnowledge); semaphore.acquire(); - assertEquals(getResponseMessage(msgListener1), "hello world3"); - assertEquals(getResponseMessage(msgListener2), "hello world4"); - assertEquals(getResponseMessage(msgListener3), "hello world3"); - assertEquals(getResponseMessage(msgListener4), "hello world4"); - assertEquals(getResponseMessage(msgListener5), "hello world3"); - assertEquals(getResponseMessage(msgListener6), "hello world4"); + assertEquals(getResponseMessage(msgListener1), DATA_VALUE_HELLO_WORLD_03); + assertEquals(getResponseMessage(msgListener2), DATA_VALUE_HELLO_WORLD_04); + assertEquals(getResponseMessage(msgListener3), DATA_VALUE_HELLO_WORLD_03); + assertEquals(getResponseMessage(msgListener4), DATA_VALUE_HELLO_WORLD_04); + assertEquals(getResponseMessage(msgListener5), DATA_VALUE_HELLO_WORLD_03); + assertEquals(getResponseMessage(msgListener6), DATA_VALUE_HELLO_WORLD_04); } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); fail(); diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java index ca776ee168..b240c77eb9 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2TcpServerSuccessScenarioTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_FRAME_STREAM_03; +import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.DATA_VALUE_HELLO_WORLD_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.END_SLEEP_TIME; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.HEADER_FRAME_STREAM_03; import static io.ballerina.stdlib.http.transport.http2.frameleveltests.FrameLevelTestUtils.SETTINGS_FRAME; @@ -67,7 +68,7 @@ private void testSuccessfulConnection() { } catch (InterruptedException e) { LOGGER.error("Interrupted exception occurred"); } - assertEquals(getResponseMessage(msgListener), "hello world3"); + assertEquals(getResponseMessage(msgListener), DATA_VALUE_HELLO_WORLD_03); } private void runTcpServer(int port) { From feeca1fbb6a4e9b33ff2074d822c532a837c897c Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 26 Jan 2024 00:40:02 +0530 Subject: [PATCH 43/46] Fix review comments --- ballerina/http_client_connection_pool.bal | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ballerina/http_client_connection_pool.bal b/ballerina/http_client_connection_pool.bal index fd4fbcbb45..0dc4bb1526 100644 --- a/ballerina/http_client_connection_pool.bal +++ b/ballerina/http_client_connection_pool.bal @@ -20,6 +20,8 @@ configurable int maxActiveConnections = -1; configurable int maxIdleConnections = 100; configurable decimal waitTime = 30; configurable int maxActiveStreamsPerConnection = 100; +configurable decimal minEvictableIdleTime = 300; +configurable decimal timeBetweenEvictionRuns = 30; # Configurations for managing HTTP client connection pool. # @@ -31,8 +33,8 @@ configurable int maxActiveStreamsPerConnection = 100; # + timeBetweenEvictionRuns - Time between eviction runs in seconds. Default value is 30 seconds # + minIdleTimeInStaleState - Minimum time in seconds for a connection to be kept open which has received a GOAWAY. # This only applies for HTTP/2. Default value is 5 minutes. If the value is set to -1, -# the connection will not be closed until all existing streams are completed -# + timeBetweenStaleCheck - Time between the connection stale check run in seconds. This only applies for HTTP/2. +# the connection will be closed after all in-flight streams are completed +# + timeBetweenStaleCheck - Time between the connection stale eviction runs in seconds. This only applies for HTTP/2. # Default value is 30 seconds public type PoolConfiguration record {| int maxActiveConnections = maxActiveConnections; From 29f2e6ea9838e2aa92ce90c4fdeb36809b3bab24 Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Fri, 26 Jan 2024 10:50:25 +0530 Subject: [PATCH 44/46] Update stale eviction variables --- ballerina/http_client_connection_pool.bal | 6 ++++-- .../io/ballerina/stdlib/http/api/HttpConstants.java | 2 +- .../java/io/ballerina/stdlib/http/api/HttpUtil.java | 6 +++--- .../sender/channel/pool/PoolConfiguration.java | 10 +++++----- .../sender/http2/Http2ConnectionManager.java | 4 ++-- ...ectionEvictionAfterTcpServerGoAwayScenarioTest.java | 4 ++-- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/ballerina/http_client_connection_pool.bal b/ballerina/http_client_connection_pool.bal index 0dc4bb1526..3e78d05a94 100644 --- a/ballerina/http_client_connection_pool.bal +++ b/ballerina/http_client_connection_pool.bal @@ -22,6 +22,8 @@ configurable decimal waitTime = 30; configurable int maxActiveStreamsPerConnection = 100; configurable decimal minEvictableIdleTime = 300; configurable decimal timeBetweenEvictionRuns = 30; +configurable decimal minIdleTimeInStaleState = 300; +configurable decimal timeBetweenStaleEviction = 30; # Configurations for managing HTTP client connection pool. # @@ -34,7 +36,7 @@ configurable decimal timeBetweenEvictionRuns = 30; # + minIdleTimeInStaleState - Minimum time in seconds for a connection to be kept open which has received a GOAWAY. # This only applies for HTTP/2. Default value is 5 minutes. If the value is set to -1, # the connection will be closed after all in-flight streams are completed -# + timeBetweenStaleCheck - Time between the connection stale eviction runs in seconds. This only applies for HTTP/2. +# + timeBetweenStaleEviction - Time between the connection stale eviction runs in seconds. This only applies for HTTP/2. # Default value is 30 seconds public type PoolConfiguration record {| int maxActiveConnections = maxActiveConnections; @@ -44,7 +46,7 @@ public type PoolConfiguration record {| decimal minEvictableIdleTime = 300; decimal timeBetweenEvictionRuns = 30; decimal minIdleTimeInStaleState = 300; - decimal timeBetweenStaleCheck = 30; + decimal timeBetweenStaleEviction = 30; |}; //This is a hack to get the global map initialized, without involving locking. diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java index 766ee36ffa..c491ba9b8e 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java @@ -513,7 +513,7 @@ public final class HttpConstants { public static final BString CONNECTION_POOLING_IDLE_TIME_STALE_STATE = StringUtils.fromString( "minIdleTimeInStaleState"); public static final BString CONNECTION_POOLING_TIME_BETWEEN_STALE_CHECK_RUNS = StringUtils.fromString( - "timeBetweenStaleCheck"); + "timeBetweenStaleEviction"); public static final String HTTP_CLIENT_CONNECTION_POOL = "PoolConfiguration"; public static final String CONNECTION_MANAGER = "ConnectionManager"; public static final int POOL_CONFIG_INDEX = 1; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java index f0e0fb3933..0e983ed339 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java @@ -1368,11 +1368,11 @@ public static void populatePoolingConfig(BMap poolRecord, PoolConfiguration pool poolConfiguration.setMinIdleTimeInStaleState(minIdleTimeInStaleState < -1 ? -1 : (long) minEvictableIdleTime * 1000); - double timeBetweenStaleCheck = + double timeBetweenStaleEviction = ((BDecimal) poolRecord.get(HttpConstants.CONNECTION_POOLING_TIME_BETWEEN_STALE_CHECK_RUNS)) .floatValue(); - if (timeBetweenStaleCheck > 0) { - poolConfiguration.setTimeBetweenStaleCheck((long) timeBetweenStaleCheck * 1000); + if (timeBetweenStaleEviction > 0) { + poolConfiguration.setTimeBetweenStaleEviction((long) timeBetweenStaleEviction * 1000); } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java index 06db1077a3..bbc94f93fb 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/channel/pool/PoolConfiguration.java @@ -36,7 +36,7 @@ public class PoolConfiguration { private long maxWaitTime = 60000L; private int http2MaxActiveStreamsPerConnection = Integer.MAX_VALUE; private long minIdleTimeInStaleState = 300000; - private long timeBetweenStaleCheck = 30000; + private long timeBetweenStaleEviction = 30000; public PoolConfiguration() { } @@ -153,11 +153,11 @@ public void setMinIdleTimeInStaleState(long minIdleTimeInStaleState) { this.minIdleTimeInStaleState = minIdleTimeInStaleState; } - public long getTimeBetweenStaleCheck() { - return timeBetweenStaleCheck; + public long getTimeBetweenStaleEviction() { + return timeBetweenStaleEviction; } - public void setTimeBetweenStaleCheck(long timeBetweenStaleCheck) { - this.timeBetweenStaleCheck = timeBetweenStaleCheck; + public void setTimeBetweenStaleEviction(long timeBetweenStaleEviction) { + this.timeBetweenStaleEviction = timeBetweenStaleEviction; } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java index 2631a3b5c5..9ea1d96166 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java +++ b/native/src/main/java/io/ballerina/stdlib/http/transport/contractimpl/sender/http2/Http2ConnectionManager.java @@ -199,8 +199,8 @@ public void closeChannelAndEvict(Http2ClientChannel http2ClientChannel) { .close(new DefaultPromise(new DefaultEventLoop())); } }; - timer.schedule(timerTask, poolConfiguration.getTimeBetweenStaleCheck(), - poolConfiguration.getTimeBetweenStaleCheck()); + timer.schedule(timerTask, poolConfiguration.getTimeBetweenStaleEviction(), + poolConfiguration.getTimeBetweenStaleEviction()); } private Http2ChannelPool.PerRouteConnectionPool fetchPerRoutePool(HttpRoute httpRoute) { diff --git a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java index 2bf163c930..a2a1ef1416 100644 --- a/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java +++ b/native/src/test/java/io/ballerina/stdlib/http/transport/http2/frameleveltests/Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest.java @@ -78,11 +78,11 @@ public class Http2ConnectionEvictionAfterTcpServerGoAwayScenarioTest { private int numOfConnections = 0; public HttpClientConnector setupHttp2PriorKnowledgeClient(long minIdleTimeInStaleState, - long timeBetweenStaleCheck) { + long timeBetweenStaleEviction) { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); PoolConfiguration poolConfiguration = new PoolConfiguration(); poolConfiguration.setMinIdleTimeInStaleState(minIdleTimeInStaleState); - poolConfiguration.setTimeBetweenStaleCheck(timeBetweenStaleCheck); + poolConfiguration.setTimeBetweenStaleEviction(timeBetweenStaleEviction); TransportsConfiguration transportsConfiguration = new TransportsConfiguration(); SenderConfiguration senderConfiguration = new SenderConfiguration(); senderConfiguration.setPoolConfiguration(poolConfiguration); From dd9c75543b2394303ccf6f9677e0d3646ed9440c Mon Sep 17 00:00:00 2001 From: dilanSachi Date: Mon, 29 Jan 2024 08:42:14 +0530 Subject: [PATCH 45/46] Set configurable values to pool config --- ballerina/http_client_connection_pool.bal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/http_client_connection_pool.bal b/ballerina/http_client_connection_pool.bal index 3e78d05a94..25a74d1e31 100644 --- a/ballerina/http_client_connection_pool.bal +++ b/ballerina/http_client_connection_pool.bal @@ -43,10 +43,10 @@ public type PoolConfiguration record {| int maxIdleConnections = maxIdleConnections; decimal waitTime = waitTime; int maxActiveStreamsPerConnection = maxActiveStreamsPerConnection; - decimal minEvictableIdleTime = 300; - decimal timeBetweenEvictionRuns = 30; - decimal minIdleTimeInStaleState = 300; - decimal timeBetweenStaleEviction = 30; + decimal minEvictableIdleTime = minEvictableIdleTime; + decimal timeBetweenEvictionRuns = timeBetweenEvictionRuns; + decimal minIdleTimeInStaleState = minIdleTimeInStaleState; + decimal timeBetweenStaleEviction = timeBetweenStaleEviction; |}; //This is a hack to get the global map initialized, without involving locking. From e48aca0c07e7a0ffdc3012e21fb69e16aa88ebe0 Mon Sep 17 00:00:00 2001 From: Dilan Sachintha Nayanajith Date: Mon, 29 Jan 2024 08:43:04 +0530 Subject: [PATCH 46/46] Update changelog.md Co-authored-by: Krishnananthalingam Tharmigan <63336800+TharmiganK@users.noreply.github.com> --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 3c3ec482da..3dc64a4314 100644 --- a/changelog.md +++ b/changelog.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - [Expose HTTP connection eviction configurations in the client level](https://github.com/ballerina-platform/ballerina-library/issues/5951) -- [Added a way to handle GoAway frames in HTTP/2 clients](https://github.com/ballerina-platform/ballerina-library/issues/4806) +- [Handle GO_AWAY received HTTP/2 clients gracefully](https://github.com/ballerina-platform/ballerina-library/issues/4806) ### Fixed - [Remove unused import from Http2StateUtil](https://github.com/ballerina-platform/ballerina-library/issues/5966)