From 70b1fd40efe0f15c8797851700a24995c6b242bb Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 10 Oct 2023 11:26:55 +1100 Subject: [PATCH] fix infinite recursion in server dump with Path Signed-off-by: Lachlan Roberts --- .../jetty/util/component/Dumpable.java | 10 ++++- .../tests/distribution/DemoModulesTests.java | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java index 6b41542a7e5..7c728f5a36f 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java @@ -15,10 +15,12 @@ package org.eclipse.jetty.util.component; import java.io.IOException; import java.lang.reflect.Array; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.stream.Stream; import org.eclipse.jetty.util.StringUtil; @@ -155,7 +157,8 @@ public interface Dumpable { dumpContainer(out, indent, (Container)object, extras == 0); } - if (object instanceof Iterable) + // Dump an Iterable Path because it may contain itself. + if (object instanceof Iterable && !(object instanceof Path)) { dumpIterable(out, indent, (Iterable)object, extras == 0); } @@ -231,12 +234,15 @@ public interface Dumpable } } } - + static void dumpIterable(Appendable out, String indent, Iterable iterable, boolean last) throws IOException { for (Iterator i = iterable.iterator(); i.hasNext(); ) { Object item = i.next(); + // Safety net to stop iteration when an Iterable contains itself e.g. Path. + if (Objects.equals(item, iterable)) + return; String nextIndent = indent + ((i.hasNext() || !last) ? "| " : " "); out.append(indent).append("+: "); if (item instanceof Dumpable) diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java index 7e0bf3f6063..24cc52e14a2 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java @@ -652,4 +652,48 @@ public class DemoModulesTests extends AbstractJettyHomeTest } } } + + @ParameterizedTest + @MethodSource("provideEnvironmentsToTest") + public void testJettyDemo(String env) throws Exception + { + Path jettyBase = newTestJettyBaseDirectory(); + String jettyVersion = System.getProperty("jettyVersion"); + JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .jettyBase(jettyBase) + .build(); + + int httpPort = distribution.freePort(); + int sslPort = distribution.freePort(); + + String[] argsConfig = { + "--add-modules=http," + toEnvironment("demos", env) + }; + + try (JettyHomeTester.Run runConfig = distribution.start(argsConfig)) + { + assertTrue(runConfig.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); + assertEquals(0, runConfig.getExitValue()); + + String[] argsStart = { + "jetty.http.port=" + httpPort, + "jetty.ssl.port=" + sslPort, + "jetty.server.dumpAfterStart=true" + }; + + try (JettyHomeTester.Run runStart = distribution.start(argsStart)) + { + assertTrue(runStart.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS)); + startHttpClient(); + String baseURI = "http://localhost:%d/%s-test".formatted(httpPort, env); + + ContentResponse response = client.POST(baseURI + "/dump/info").send(); + assertEquals(HttpStatus.OK_200, response.getStatus(), new ResponseDetails(response)); + assertThat(response.getContentAsString(), containsString("Dump Servlet")); + assertThat(response.getContentAsString(), containsString("context-override-example: a context value")); + } + } + } + }