| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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 org.apache.catalina.manager; |
| |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.nio.charset.StandardCharsets; |
| |
| import jakarta.servlet.http.HttpServletResponse; |
| |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| import static org.apache.catalina.startup.SimpleHttpClient.CRLF; |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleEvent; |
| import org.apache.catalina.LifecycleListener; |
| import org.apache.catalina.authenticator.TestBasicAuthParser.BasicAuthHeader; |
| import org.apache.catalina.realm.MemoryRealm; |
| import org.apache.catalina.realm.MessageDigestCredentialHandler; |
| import org.apache.catalina.startup.Catalina; |
| import org.apache.catalina.startup.HostConfig; |
| import org.apache.catalina.startup.SimpleHttpClient; |
| import org.apache.catalina.startup.Tomcat; |
| import org.apache.catalina.startup.TomcatBaseTest; |
| import org.apache.catalina.storeconfig.StoreConfigLifecycleListener; |
| import org.apache.catalina.util.IOTools; |
| import org.apache.catalina.util.ServerInfo; |
| import org.apache.catalina.util.URLEncoder; |
| |
| |
| public class TestManagerWebapp extends TomcatBaseTest { |
| |
| public static final String CONFIG = "<?xml version=\"1.0\" ?>" + |
| "<tomcat-users xmlns=\"http://tomcat.apache.org/xml\"" + |
| " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + |
| " xsi:schemaLocation=\"http://tomcat.apache.org/xml/tomcat-users.xsd\"" + " version=\"1.0\">" + |
| "<role rolename=\"admin\" />" + |
| "<user username=\"admin\" password=\"sekr3t\" roles=\"manager-gui,manager-script,manager-jmx,manager-status,admin-gui,admin-script\" />" + |
| "</tomcat-users>"; |
| |
| /** |
| * Integration test for the manager webapp (verify all main Servlets are working). |
| * |
| * @throws Exception if an error occurs |
| */ |
| @Test |
| public void testServlets() throws Exception { |
| // Avoid possible locking failure due to deployment with the manager in testDeploy |
| ignoreTearDown = true; |
| Tomcat tomcat = getTomcatInstance(); |
| tomcat.setAddDefaultWebXmlToWebapp(false); |
| tomcat.addUser("admin", "sekr3t"); |
| tomcat.addRole("admin", "manager-gui"); |
| tomcat.addRole("admin", "manager-script"); |
| tomcat.addRole("admin", "manager-jmx"); |
| tomcat.addRole("admin", "manager-status"); |
| |
| File webappDir = new File(getBuildDirectory(), "webapps"); |
| |
| // Add manager webapp |
| File appDir = new File(webappDir, "manager"); |
| tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath()); |
| |
| 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 /manager/html HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Connection: Close" + CRLF + |
| CRLF |
| }); |
| // @formatter:on |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_UNAUTHORIZED, client.getStatusCode()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/html 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().contains("/manager/css/manager.css")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/status 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().contains("MiB")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/jmxproxy 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().contains("Tomcat:type=ThreadPool,name=")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text 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().contains(" - ")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/sessions?path=/manager 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().contains("[1]")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/resources 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().contains(" - ")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/serverinfo 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().contains("[" + ServerInfo.getServerInfo() + "]")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/vminfo 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().contains("vmName: ")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/threaddump 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().contains("http-")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/list 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().contains("/manager:running")); |
| |
| tomcat.stop(); |
| } |
| |
| @Test |
| public void testDeploy() throws Exception { |
| // Avoid possible locking failure due to deployment with the manager |
| ignoreTearDown = true; |
| Tomcat tomcat = getTomcatInstance(); |
| tomcat.setAddDefaultWebXmlToWebapp(false); |
| tomcat.getServer().addLifecycleListener(new StoreConfigLifecycleListener()); |
| |
| File configFile = new File(getTemporaryDirectory(), "tomcat-users-manager-deploy.xml"); |
| try (PrintWriter writer = new PrintWriter(configFile)) { |
| writer.write(CONFIG); |
| } |
| addDeleteOnTearDown(configFile); |
| |
| MemoryRealm memoryRealm = new MemoryRealm(); |
| memoryRealm.setCredentialHandler(new MessageDigestCredentialHandler()); |
| memoryRealm.setPathname(configFile.getAbsolutePath()); |
| tomcat.getEngine().setRealm(memoryRealm); |
| |
| File webappDir = new File(getBuildDirectory(), "webapps"); |
| |
| // Add manager webapp |
| File appDir = new File(webappDir, "manager"); |
| Context ctx = tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath()); |
| |
| // Add host config otherwise there's no JMX deployer bean |
| HostConfig hostConfig = new HostConfig(); |
| ctx.getParent().addLifecycleListener(hostConfig); |
| |
| tomcat.start(); |
| |
| SimpleHttpClient client = new SimpleHttpClient() { |
| // 10s default too low for some CI systems |
| @Override |
| public void connect() throws IOException { |
| connect(30000, 30000); |
| } |
| |
| @Override |
| public boolean isResponseBodyOK() { |
| return true; |
| } |
| }; |
| client.setPort(getPort()); |
| String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); |
| |
| appDir = new File(webappDir, "examples"); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/deploy?war=" + URLEncoder.QUERY.encode(appDir.getAbsolutePath(), StandardCharsets.UTF_8) + " 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().contains("OK - ")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Connection: Close" + CRLF + |
| CRLF |
| }); |
| // @formatter:on |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); |
| Assert.assertTrue(client.getResponseBody().contains("/examples/servlets/servlet/RequestInfoExample")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/stop?path=/examples 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()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Connection: Close" + CRLF + |
| CRLF |
| }); |
| // @formatter:on |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, client.getStatusCode()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/start?path=/examples 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()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Connection: Close" + CRLF + |
| CRLF |
| }); |
| // @formatter:on |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); |
| Assert.assertTrue(client.getResponseBody().contains("/examples/servlets/servlet/RequestInfoExample")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/save 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()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/save?path=/examples 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()); |
| |
| File serverXml = new File(tomcat.getServer().getCatalinaBase(), Catalina.SERVER_XML); |
| Assert.assertTrue(serverXml.canRead()); |
| addDeleteOnTearDown(serverXml); |
| String serverXmlDump; |
| try (FileReader reader = new FileReader(serverXml); StringWriter writer = new StringWriter()) { |
| IOTools.flow(reader, writer); |
| serverXmlDump = writer.toString(); |
| } |
| Assert.assertTrue(serverXmlDump.contains("StoreConfigLifecycleListener")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/reload?path=/examples 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()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/list 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("/examples:running")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + |
| "Host: localhost" + 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("/examples/servlets/servlet/RequestInfoExample")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/list 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("/examples:running")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/undeploy?path=/examples 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()); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/list 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.assertFalse(client.getResponseBody(), client.getResponseBody().contains("/examples:running")); |
| |
| // @formatter:off |
| client.setRequest(new String[] { |
| "GET /manager/text/findleaks?statusLine=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()); |
| |
| tomcat.stop(); |
| } |
| |
| |
| /* |
| * Test case for <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=57700">Bug 57700</a>. |
| */ |
| @Test |
| public void testBug57700() throws Exception { |
| ignoreTearDown = true; |
| Tomcat tomcat = getTomcatInstance(); |
| tomcat.setAddDefaultWebXmlToWebapp(false); |
| File configFile = new File(getTemporaryDirectory(), "tomcat-users-manager-lifecycle.xml"); |
| try (PrintWriter writer = new PrintWriter(configFile)) { |
| writer.write(CONFIG); |
| } |
| addDeleteOnTearDown(configFile); |
| |
| MemoryRealm memoryRealm = new MemoryRealm(); |
| memoryRealm.setCredentialHandler(new MessageDigestCredentialHandler()); |
| memoryRealm.setPathname(configFile.getAbsolutePath()); |
| tomcat.getEngine().setRealm(memoryRealm); |
| |
| File webappDir = new File(getBuildDirectory(), "webapps"); |
| |
| File appDir = new File(webappDir, "manager"); |
| Context ctx = tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath()); |
| |
| HostConfig hostConfig = new HostConfig(); |
| ctx.getParent().addLifecycleListener(hostConfig); |
| |
| File appRoot = new File(webappDir, "bug57700"); |
| Assert.assertTrue(appRoot.mkdirs() && appRoot.isDirectory()); |
| addDeleteOnTearDown(appRoot); |
| |
| try (@SuppressWarnings("unused") TomcatBaseTest.ContainerInjector ignored = |
| TomcatBaseTest.ContainerInjector.inject(ctx.getParent(), |
| c -> c.getPath().equals("/bug57700"), |
| c -> { |
| c.addLifecycleListener(new FailOnceListener()); |
| Tomcat.initWebappDefaults(c); |
| })) { |
| |
| tomcat.start(); |
| SimpleHttpClient client = new SimpleHttpClient() { |
| @Override |
| public void connect() throws IOException { |
| connect(30000, 30000); |
| } |
| |
| @Override |
| public boolean isResponseBodyOK() { |
| return true; |
| } |
| }; |
| |
| client.setPort(getPort()); |
| String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); |
| |
| client.setRequest(new String[]{ |
| "GET /manager/text/deploy?war=" + URLEncoder.QUERY.encode(appRoot.getAbsolutePath(), StandardCharsets.UTF_8) + " HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Authorization: " + basicHeader + CRLF + |
| "Connection: Close" + CRLF + CRLF |
| }); |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); |
| |
| client.setRequest(new String[]{ |
| "GET /bug57700 HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Connection: Close" + CRLF + CRLF |
| }); |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, client.getStatusCode()); |
| |
| client.setRequest(new String[]{ |
| "GET /manager/text/start?path=/bug57700 HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Authorization: " + basicHeader + CRLF + |
| "Connection: Close" + CRLF + CRLF |
| }); |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); |
| |
| client.setRequest(new String[]{ |
| "GET /bug57700 HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Connection: Close" + CRLF + CRLF |
| }); |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_FOUND, client.getStatusCode()); |
| |
| client.setRequest(new String[] { |
| "GET /manager/text/undeploy?path=/bug57700 HTTP/1.1" + CRLF + |
| "Host: localhost" + CRLF + |
| "Authorization: " + basicHeader + CRLF + |
| "Connection: Close" + CRLF + |
| CRLF |
| }); |
| client.connect(); |
| client.processRequest(true); |
| Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); |
| |
| tomcat.stop(); |
| } |
| } |
| |
| private static class FailOnceListener implements LifecycleListener { |
| private volatile boolean firstRun = true; |
| @Override |
| public void lifecycleEvent(LifecycleEvent event) { |
| if (event.getLifecycle() instanceof Context) { |
| if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType()) && firstRun) { |
| firstRun = false; |
| throw new RuntimeException("Configuration failure in first run only"); |
| } |
| } |
| } |
| } |
| } |