This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 435ae3ae9be10a10ba05738badf1346a55408acf Author: opencode <[email protected]> AuthorDate: Wed Oct 7 11:31:59 2026 +0200 Allow re-adding a host that was previously removed with manager.xml Removing a host does not physically remove the host configuration directory. When the removed host had been created with the manager attribute, the manager.xml copy installed in the configuration directory for that host remained behind. Adding a host again with the same name and manager=true then failed with "Couldn't install manager.xml" because Files.copy() refuses to replace an existing target, so the host was never created. This is a common flow since the HTML add form checks the manager option by default. Copy manager.xml with StandardCopyOption.REPLACE_EXISTING so the template shipped with the running host manager application overwrites the leftover file, matching the behaviour of a fresh add. Add a test covering add, remove and re-add with manager=true. --- .../catalina/manager/host/HostManagerServlet.java | 6 +- .../catalina/manager/TestHostManagerWebapp.java | 95 ++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/java/org/apache/catalina/manager/host/HostManagerServlet.java b/java/org/apache/catalina/manager/host/HostManagerServlet.java index 66fc50cfc4..45b105db0e 100644 --- a/java/org/apache/catalina/manager/host/HostManagerServlet.java +++ b/java/org/apache/catalina/manager/host/HostManagerServlet.java @@ -23,6 +23,7 @@ import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.StringTokenizer; import javax.management.InstanceNotFoundException; @@ -386,7 +387,10 @@ public class HostManagerServlet extends HttpServlet implements ContainerServlet return; } Path dest = new File(configBaseFile, "manager.xml").toPath(); - Files.copy(is, dest); + // Replace any manager.xml left behind by a previous host + // with the same name. Removing a host does not delete the + // configuration directory. + Files.copy(is, dest, StandardCopyOption.REPLACE_EXISTING); } catch (IOException ioe) { writer.println(smClient.getString("hostManagerServlet.managerXml")); return; diff --git a/test/org/apache/catalina/manager/TestHostManagerWebapp.java b/test/org/apache/catalina/manager/TestHostManagerWebapp.java index e2af5fca9a..9330254a2d 100644 --- a/test/org/apache/catalina/manager/TestHostManagerWebapp.java +++ b/test/org/apache/catalina/manager/TestHostManagerWebapp.java @@ -297,4 +297,99 @@ public class TestHostManagerWebapp extends TomcatBaseTest { tomcat.stop(); } + + + /* + * Removing a host leaves the configuration directory in place. Re-adding + * a host with the same name using manager=true must then replace the + * leftover manager.xml rather than failing to install it. + */ + @Test + public void testAddRemoveReAddWithManager() throws Exception { + System.setProperty("catalina.home", getBuildDirectory().getAbsolutePath()); + Tomcat tomcat = getTomcatInstance(); + tomcat.setAddDefaultWebXmlToWebapp(false); + + File configFile = new File(getTemporaryDirectory(), "tomcat-users-host-manager-readd.xml"); + try (PrintWriter writer = new PrintWriter(configFile)) { + writer.write(TestManagerWebapp.CONFIG); + } + addDeleteOnTearDown(configFile); + + MemoryRealm memoryRealm = new MemoryRealm(); + memoryRealm.setCredentialHandler(new MessageDigestCredentialHandler()); + memoryRealm.setPathname(configFile.getAbsolutePath()); + tomcat.getEngine().setRealm(memoryRealm); + + File confFolder = new File(getTemporaryDirectory(), "conf"); + confFolder.mkdirs(); + File webappDir = new File(getBuildDirectory(), "webapps"); + + File appDir = new File(webappDir, "host-manager"); + tomcat.addWebapp(null, "/host-manager", appDir.getAbsolutePath()); + + getProgrammaticRootContext(); + + tomcat.start(); + + SimpleHttpClient client = new SimpleHttpClient() { + @Override + public boolean isResponseBodyOK() { + return true; + } + }; + client.setPort(getPort()); + String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); + + // @formatter:off + client.setRequest(new String[] { + "GET /host-manager/text/add?name=rehost&manager=true HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + Assert.assertTrue(client.getResponseBody(), + client.getResponseBody().contains("OK - Host [rehost] added")); + File managerXml = new File(confFolder, tomcat.getEngine().getName() + "/rehost/manager.xml"); + Assert.assertTrue(managerXml.getAbsolutePath(), managerXml.isFile()); + + // @formatter:off + client.setRequest(new String[] { + "GET /host-manager/text/remove?name=rehost HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + Assert.assertTrue(client.getResponseBody(), + client.getResponseBody().contains("OK - Removed host [rehost]")); + // The configuration directory is intentionally left in place + Assert.assertTrue(managerXml.getAbsolutePath(), managerXml.isFile()); + + // @formatter:off + client.setRequest(new String[] { + "GET /host-manager/text/add?name=rehost&manager=true HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + Assert.assertTrue(client.getResponseBody(), + client.getResponseBody().contains("OK - Host [rehost] added")); + + tomcat.stop(); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
