diff --git a/jetty-server/src/main/config/modules/stats.mod b/jetty-server/src/main/config/modules/stats.mod index a6289c4174bb..7ecdb25614c3 100644 --- a/jetty-server/src/main/config/modules/stats.mod +++ b/jetty-server/src/main/config/modules/stats.mod @@ -9,6 +9,10 @@ handler [depend] server +servlet + +[lib] +lib/jetty-util-ajax-${jetty.version}.jar [xml] etc/jetty-stats.xml diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java index fbf0ebaed4a5..56e9ef98ebaa 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/StatisticsServlet.java @@ -319,7 +319,7 @@ private CharSequence generateResponse(OutputProducer outputProducer) { connectorDetail.put("statsOn", true); connectorDetail.put("connections", connectionStats.getConnectionsTotal()); - connectorDetail.put("connectionsOpen>", connectionStats.getConnections()); + connectorDetail.put("connectionsOpen", connectionStats.getConnections()); connectorDetail.put("connectionsOpenMax", connectionStats.getConnectionsMax()); connectorDetail.put("connectionsDurationMean", connectionStats.getConnectionDurationMean()); connectorDetail.put("connectionsDurationMax", connectionStats.getConnectionDurationMax()); diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml index 2edaa3136641..755695cb6fd6 100644 --- a/tests/test-distribution/pom.xml +++ b/tests/test-distribution/pom.xml @@ -123,6 +123,12 @@ ${project.version} test + + org.eclipse.jetty + jetty-util-ajax + ${project.version} + test + org.eclipse.jetty.websocket websocket-client diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/StatsTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/StatsTests.java new file mode 100644 index 000000000000..e62595029d82 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/StatsTests.java @@ -0,0 +1,170 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution; + +import java.io.ByteArrayInputStream; +import java.net.URI; +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.util.ajax.JSON; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class StatsTests extends AbstractDistributionTest +{ + @Test + public void testStatsServlet() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=resources,server,http,webapp,deploy,stats" + }; + try (DistributionTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + Path webappsDir = distribution.getJettyBase().resolve("webapps"); + FS.ensureDirExists(webappsDir.resolve("demo")); + FS.ensureDirExists(webappsDir.resolve("demo/WEB-INF")); + + distribution.installBaseResource("stats-webapp/index.html", "webapps/demo/index.html"); + distribution.installBaseResource("stats-webapp/WEB-INF/web.xml", "webapps/demo/WEB-INF/web.xml"); + + int port = distribution.freePort(); + String[] args2 = { + "jetty.http.port=" + port + }; + try (DistributionTester.Run run2 = distribution.start(args2)) + { + assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + + startHttpClient(); + + ContentResponse response; + URI serverBaseURI = URI.create("http://localhost:" + port); + + response = client.GET(serverBaseURI.resolve("/demo/index.html")); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("

Stats Demo

")); + + // --------------- + // Test XML accept + response = client.newRequest(serverBaseURI.resolve("/demo/stats")) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT, "text/xml") + .send(); + assertEquals(HttpStatus.OK_200, response.getStatus()); + + assertThat("Response.contentType", response.getHeaders().get(HttpHeader.CONTENT_TYPE), containsString("text/xml")); + + // Parse it, make sure it's well formed. + DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); + docBuilderFactory.setValidating(false); + DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); + try (ByteArrayInputStream input = new ByteArrayInputStream(response.getContent())) + { + Document doc = docBuilder.parse(input); + assertNotNull(doc); + assertEquals("statistics", doc.getDocumentElement().getNodeName()); + } + + // --------------- + // Test JSON accept + response = client.newRequest(serverBaseURI.resolve("/demo/stats")) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT, "application/json") + .send(); + assertEquals(HttpStatus.OK_200, response.getStatus()); + + assertThat("Response.contentType", response.getHeaders().get(HttpHeader.CONTENT_TYPE), containsString("application/json")); + + Object doc = JSON.parse(response.getContentAsString()); + assertNotNull(doc); + assertThat(doc, instanceOf(Map.class)); + Map docMap = (Map)doc; + assertEquals(4, docMap.size()); + assertNotNull(docMap.get("requests")); + assertNotNull(docMap.get("responses")); + assertNotNull(docMap.get("connections")); + assertNotNull(docMap.get("memory")); + + // --------------- + // Test TEXT accept + response = client.newRequest(serverBaseURI.resolve("/demo/stats")) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT, "text/plain") + .send(); + assertEquals(HttpStatus.OK_200, response.getStatus()); + + assertThat("Response.contentType", response.getHeaders().get(HttpHeader.CONTENT_TYPE), containsString("text/plain")); + + String textContent = response.getContentAsString(); + assertThat(textContent, containsString("requests: ")); + assertThat(textContent, containsString("responses: ")); + assertThat(textContent, containsString("connections: ")); + assertThat(textContent, containsString("memory: ")); + + // --------------- + // Test HTML accept + response = client.newRequest(serverBaseURI.resolve("/demo/stats")) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT, "text/html") + .send(); + assertEquals(HttpStatus.OK_200, response.getStatus()); + + assertThat("Response.contentType", response.getHeaders().get(HttpHeader.CONTENT_TYPE), containsString("text/html")); + + String htmlContent = response.getContentAsString(); + // Look for things that indicate it's a well formed HTML output + assertThat(htmlContent, containsString("")); + assertThat(htmlContent, containsString("")); + assertThat(htmlContent, containsString("requests: ")); + assertThat(htmlContent, containsString("responses: ")); + assertThat(htmlContent, containsString("connections: ")); + assertThat(htmlContent, containsString("memory: ")); + assertThat(htmlContent, containsString("")); + assertThat(htmlContent, containsString("")); + } + } + } +} diff --git a/tests/test-distribution/src/test/resources/stats-webapp/WEB-INF/web.xml b/tests/test-distribution/src/test/resources/stats-webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..f2046a59a5ba --- /dev/null +++ b/tests/test-distribution/src/test/resources/stats-webapp/WEB-INF/web.xml @@ -0,0 +1,17 @@ + + stats-demo + + Stats + org.eclipse.jetty.servlet.StatisticsServlet + 1 + + + + Stats + /stats + + \ No newline at end of file diff --git a/tests/test-distribution/src/test/resources/stats-webapp/index.html b/tests/test-distribution/src/test/resources/stats-webapp/index.html new file mode 100644 index 000000000000..d203fae78fac --- /dev/null +++ b/tests/test-distribution/src/test/resources/stats-webapp/index.html @@ -0,0 +1,8 @@ + + + stats-demo + + +

Stats Demo

+ + \ No newline at end of file