From f21cfce91282925dc6a077233a27bc4bd678f7cc Mon Sep 17 00:00:00 2001 From: Paul Nicolucci Date: Thu, 3 Apr 2025 13:07:59 -0400 Subject: [PATCH 1/3] Add server to test tcpOptions inactivityTimeout --- .../InactivityTimeout/bootstrap.properties | 14 +++++++++++ .../servers/InactivityTimeout/server.xml | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/bootstrap.properties create mode 100644 dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/server.xml diff --git a/dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/bootstrap.properties b/dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/bootstrap.properties new file mode 100644 index 000000000000..8445cf41a9ec --- /dev/null +++ b/dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/bootstrap.properties @@ -0,0 +1,14 @@ +############################################################################### +# Copyright (c) 2025 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +############################################################################### +bootstrap.include=../testports.properties +com.ibm.ws.logging.trace.specification=*=info:TCPChannel=all +com.ibm.ws.logging.max.file.size=100 +com.ibm.ws.logging.max.files=10 +com.ibm.ws.logging.trace.format=BASIC diff --git a/dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/server.xml b/dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/server.xml new file mode 100644 index 000000000000..de331c6611f7 --- /dev/null +++ b/dev/io.openliberty.transport.http_fat/publish/servers/InactivityTimeout/server.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + servlet-3.1 + + + From 0dcdc40c9de6263d4167d165e4c4ef60780b4608 Mon Sep 17 00:00:00 2001 From: Paul Nicolucci Date: Thu, 3 Apr 2025 21:39:17 -0400 Subject: [PATCH 2/3] Add inactivityTimeout so it can be set programmatically in tests --- .../ibm/websphere/simplicity/config/TcpOptions.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dev/fattest.simplicity/src/com/ibm/websphere/simplicity/config/TcpOptions.java b/dev/fattest.simplicity/src/com/ibm/websphere/simplicity/config/TcpOptions.java index 5e254a4030ac..f3d7286cb7e0 100644 --- a/dev/fattest.simplicity/src/com/ibm/websphere/simplicity/config/TcpOptions.java +++ b/dev/fattest.simplicity/src/com/ibm/websphere/simplicity/config/TcpOptions.java @@ -24,6 +24,7 @@ public class TcpOptions extends ConfigElement { private String addressExcludeList; private String hostNameIncludeList; private String hostNameExcludeList; + private String inactivityTimeout; private Integer maxOpenConnections; public Boolean isSoReuseAddr() { @@ -46,6 +47,10 @@ public String getHostNameExcludeList() { return hostNameExcludeList; } + public String getInactivityTimeout() { + return inactivityTimeout; + } + public Integer getMaxOpenConnections() { return maxOpenConnections; } @@ -75,6 +80,11 @@ public void setHostNameExcludeList(String hostNameExcludeList) { this.hostNameExcludeList = hostNameExcludeList; } + @XmlAttribute + public void setInactivityTimeout(String inactivityTimeout) { + this.inactivityTimeout = inactivityTimeout; + } + @XmlAttribute public void setMaxOpenConnections(Integer maxOpenConnections) { this.maxOpenConnections = maxOpenConnections; @@ -95,6 +105,8 @@ public String toString() { buf.append("hostNameIncludeList=\"" + hostNameIncludeList + "\" "); if (getHostNameExcludeList() != null) buf.append("hostNameExcludeList=\"" + hostNameExcludeList + "\" "); + if (getInactivityTimeout() != null) + buf.append("inactivityTimeout=\"" + inactivityTimeout + "\" "); if (getMaxOpenConnections() != null) buf.append("maxOpenConnections=\"" + maxOpenConnections + "\" "); buf.append("}"); From abcb003bdf1e301dfa7ae907ca775264a6e249e3 Mon Sep 17 00:00:00 2001 From: Paul Nicolucci Date: Thu, 3 Apr 2025 21:40:17 -0400 Subject: [PATCH 3/3] Add new FAT to test tcpOptions inactivityTimeout --- .../transport/http_fat/FATSuite.java | 3 +- .../http_fat/InactivityTimeoutTests.java | 250 ++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/InactivityTimeoutTests.java diff --git a/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/FATSuite.java b/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/FATSuite.java index 99eeaf517846..24f0f1206e7b 100644 --- a/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/FATSuite.java +++ b/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/FATSuite.java @@ -23,7 +23,8 @@ @RunWith(Suite.class) @SuiteClasses({ AccessListsTests.class, - MaxOpenConnectionsTest.class + MaxOpenConnectionsTest.class, + InactivityTimeoutTests.class }) public class FATSuite { diff --git a/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/InactivityTimeoutTests.java b/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/InactivityTimeoutTests.java new file mode 100644 index 000000000000..c45ec9381d55 --- /dev/null +++ b/dev/io.openliberty.transport.http_fat/fat/src/io/openliberty/transport/http_fat/InactivityTimeoutTests.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2025 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.transport.http_fat; + +import static org.junit.Assert.assertNotNull; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.config.HttpEndpoint; +import com.ibm.websphere.simplicity.config.ServerConfiguration; + +import componenttest.annotation.ExpectedFFDC; +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.custom.junit.runner.Mode; +import componenttest.custom.junit.runner.Mode.TestMode; +import componenttest.topology.impl.LibertyServer; + +/** + * Test to ensure that the tcpOptions inactivityTimeout works. + */ +@RunWith(FATRunner.class) +@Mode(TestMode.FULL) +public class InactivityTimeoutTests { + + static final Logger LOG = Logger.getLogger(InactivityTimeoutTests.class.getName()); + + @Server("InactivityTimeout") + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + // Start the server and use the class name so we can find logs easily. + server.startServer(InactivityTimeoutTests.class.getSimpleName() + ".log"); + } + + /** + * Save the server configuration before each test, this should be the default server + * configuration. + * + * @throws Exception + */ + @Before + public void beforeTest() throws Exception { + server.saveServerConfiguration(); + } + + @AfterClass + public static void tearDown() throws Exception { + // Stop the server + if (server != null && server.isStarted()) { + //CWWKO0211E: TCP Channel defaultHttpEndpoint has been constructed with an incorrect configuration property value. Name: inactivityTimeout Value: -1 Valid Range: Minimum 0, Maximum 3600000
[4/8/25, 21:04:00:557 EDT] 0000004c com.ibm.ws.channelfw.internal.ChannelFrameworkImpl + //CWWKO0029E: An exception was generated when initializing chain CHAIN-defaultHttpEndpoint because of exception com.ibm.wsspi.channelfw.exception.ChannelException: A TCP Channel has been constructed with incorrect configuration property value, Channel Name: defaultHttpEndpoint name: inactivityTimeout value: -1 minimum Value: 0 maximum Value: 3600000 + server.stopServer("CWWKO0211E", "CWWKO0029E"); + } + } + + /** + * Restore the server configuration to the default state after each test. + * + * @throws Exception + */ + @After + public void afterTest() throws Exception { + // Restore the server to the default state. + server.setMarkToEndOfLog(); + server.setTraceMarkToEndOfDefaultTrace(); + server.restoreServerConfiguration(); + server.waitForConfigUpdateInLogUsingMark(null); + } + + /** + * The test will check the default value of inactivityTimeout by searching the trace file. + * + * The default value is 60 seconds will be logged as 60000 miliseconds. + */ + @Test + public void testInactivityTimeout_default() throws Exception { + // Validate that inActivityTimeout default is 60s. + assertNotNull("The default value of inactivityTimeout was not: 60000!", server.waitForStringInTraceUsingMark("inactivityTimeout: 60000")); + } + + /** + * The test will set inactivityTimeout to a value of 120s and validate in the trace file that + * the correct value is being used. + * + * The below configuration will be used to set inactivityTimeout to 120s: + * + * + * @throws Exception + */ + @Test + public void testInactivityTimeout_nonDefault() throws Exception { + ServerConfiguration configuration = server.getServerConfiguration(); + LOG.info("Server configuration that the test started with: " + configuration); + + HttpEndpoint httpEndpoint = configuration.getHttpEndpoints().getById("defaultHttpEndpoint"); + httpEndpoint.getTcpOptions().setInactivityTimeout("120s"); + + server.setMarkToEndOfLog(); + server.setTraceMarkToEndOfDefaultTrace(); + server.updateServerConfiguration(configuration); + server.waitForConfigUpdateInLogUsingMark(null); + + // Validate that inactivityTimeout is set to 120000 (120s). + assertNotNull("The configured value of inactivityTimeout was not 120000!", server.waitForStringInTraceUsingMark("inactivityTimeout: 120000")); + } + + /** + * The test will set inactivityTimeout to a value of -1s and validate that an error occurs. + * + * The below configuration will be used to set inactivityTimeout to 120s: + * + * + * ExpectedFFDC: + * >------Start of DE processing------ = [4/8/25, 19:35:17:901 EDT] + * >Exception = com.ibm.wsspi.channelfw.exception.ChannelException + * >Source = com.ibm.ws.tcpchannel.internal.TCPChannelConfiguration + * >probeid = 102 + * >Stack Dump = com.ibm.wsspi.channelfw.exception.ChannelException: A TCP Channel has been constructed with incorrect configuration property value, Channel Name: + * defaultHttpEndpoint name: inactivityTimeout value: -1 minimum Value: 0 maximum Value: 3600000 + * > at com.ibm.ws.tcpchannel.internal.TCPChannelConfiguration.setValues(TCPChannelConfiguration.java:553) + * + * @throws Exception + */ + @Test + @ExpectedFFDC("com.ibm.wsspi.channelfw.exception.ChannelException") + public void testInactivityTimeout_negative() throws Exception { + ServerConfiguration configuration = server.getServerConfiguration(); + LOG.info("Server configuration that the test started with: " + configuration); + + HttpEndpoint httpEndpoint = configuration.getHttpEndpoints().getById("defaultHttpEndpoint"); + httpEndpoint.getTcpOptions().setInactivityTimeout("-1"); + + server.setMarkToEndOfLog(); + server.setTraceMarkToEndOfDefaultTrace(); + server.updateServerConfiguration(configuration); + server.waitForConfigUpdateInLogUsingMark(null); + + // Validate error messages due to invalid config. + assertNotNull("CWWKO0211E was not found and should have been!", server.waitForStringInTraceUsingMark("CWWKO0211E")); + assertNotNull("CWWKO0029E was not found and should have been!", server.waitForStringInTraceUsingMark("CWWKO0029E")); + } + + /** + * The test will set the inactivitiyTimeout to a value of 10s. + * + * A socket will be opened. + * + * The test will sleep for 15s which is greater then the inactivityTimeout. + * + * The test will then send a request and validate that the response is null since the + * request should fail because the remote side of the Socket has closed due to the + * inactivityTimeout. + * + * @throws Exception + */ + //@Test + public void testInactivityTimeout() throws Exception { + String expectedResponse = "HTTP/1.1 408 Request Timeout"; + + String address = server.getHostname() + ":" + server.getHttpDefaultPort(); + + String request = "GET /" + " HTTP/1.1\r\n" + + "Host: " + address + "\r\n" + + "\r\n"; + + // Set the inactivityTimeout to 10 seconds to make testing quicker than the default 60 second timeout. + ServerConfiguration configuration = server.getServerConfiguration(); + LOG.info("Server configuration that the test started with: " + configuration); + + HttpEndpoint httpEndpoint = configuration.getHttpEndpoints().getById("defaultHttpEndpoint"); + httpEndpoint.getTcpOptions().setInactivityTimeout("10s"); + + server.setMarkToEndOfLog(); + server.setTraceMarkToEndOfDefaultTrace(); + server.updateServerConfiguration(configuration); + server.waitForConfigUpdateInLogUsingMark(null); + + // Validate that inactivityTimeout is set to 10000 (10s). + assertNotNull("The configured value of inactivityTimeout was not 10000!", server.waitForStringInTraceUsingMark("inactivityTimeout: 10000")); + + // Ensure the TCP Channel has started. + // CWWKO0219I: TCP Channel defaultHttpEndpoint has been started and is now listening for requests on host * (IPv4) port 8010. + assertNotNull("The TCP Channel was not started!", server.waitForStringInLogUsingMark("CWWKO0219I")); + + LOG.info("Creating a Socket connection."); + try (Socket socket = new Socket(server.getHostname(), server.getHttpDefaultPort())) { + + // Sleep for more than the inactivityTimeout. + //Thread.sleep(15000); + // Even without this we get a inactivitiyTimeout + + String line; + BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + OutputStream os = socket.getOutputStream(); + + // LOG.info("Read from the Socket."); + // boolean expectedResponseFound = false; + // while ((line = reader.readLine()) != null) { + // LOG.info(line); + // if (line.equals(expectedResponse)) { + // expectedResponseFound = true; + // } + // } + + // Maybe sleep here??? + + //assertTrue("The expected response: " + expectedResponse + " was not found!", expectedResponseFound); + + // Sending the request and reading the response should not work since the remote side of the Socket should have closed due to an inactivitiyTimeout! + LOG.info("Sending a request."); + os.write(request.getBytes()); + + Thread.sleep(15000); + + LOG.info("Read the response."); + while ((line = reader.readLine()) != null) { + LOG.info(line); + //if (line.equals(expectedResponse)) { + // expectedResponseFound = true; + // } + } + //assertTrue("There should be no response data since the Socket was closed on the remote side due to an inactivityTimeout: " + response, response == null); + + } + } + + // TODO: Review the tracing of the above test and validate what is happening. + // Does the test above pass without any wait on the initial socket creation? + +}