diff --git a/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HTTPServer.java b/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HTTPServer.java index 41fdec76d..f0c536fe7 100644 --- a/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HTTPServer.java +++ b/prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HTTPServer.java @@ -66,11 +66,13 @@ private HTTPServer( this.server = httpServer; this.executorService = executorService; String metricsPath = getMetricsPath(metricsHandlerPath); - registerHandler( - "/", - defaultHandler == null ? new DefaultHandler(metricsPath) : defaultHandler, - authenticator, - authenticatedSubjectAttributeName); + if (!metricsPath.equals("/")) { + registerHandler( + "/", + defaultHandler == null ? new DefaultHandler(metricsPath) : defaultHandler, + authenticator, + authenticatedSubjectAttributeName); + } registerHandler( metricsPath, new MetricsHandler(config, registry), diff --git a/prometheus-metrics-exporter-httpserver/src/test/java/io/prometheus/metrics/exporter/httpserver/HTTPServerTest.java b/prometheus-metrics-exporter-httpserver/src/test/java/io/prometheus/metrics/exporter/httpserver/HTTPServerTest.java index 9b7f658de..df53793f8 100644 --- a/prometheus-metrics-exporter-httpserver/src/test/java/io/prometheus/metrics/exporter/httpserver/HTTPServerTest.java +++ b/prometheus-metrics-exporter-httpserver/src/test/java/io/prometheus/metrics/exporter/httpserver/HTTPServerTest.java @@ -10,25 +10,41 @@ import com.sun.net.httpserver.HttpsConfigurator; import io.prometheus.metrics.model.registry.PrometheusRegistry; import io.prometheus.metrics.model.registry.PrometheusScrapeRequest; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricMetadata; import io.prometheus.metrics.model.snapshots.MetricSnapshots; import java.io.IOException; import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.security.NoSuchAlgorithmException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.security.Principal; +import java.util.List; import java.util.concurrent.Executors; import javax.net.ssl.SSLContext; import javax.security.auth.Subject; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class HTTPServerTest { + private PrometheusRegistry registry; + + @BeforeEach + void setUp() { + final MetricMetadata metadata = new MetricMetadata("my-counter"); + final CounterDataPointSnapshot dataPointSnapshot = + new CounterDataPointSnapshot(1.0, Labels.EMPTY, null, System.currentTimeMillis()); + + registry = new PrometheusRegistry(); + registry.register(() -> new CounterSnapshot(metadata, List.of(dataPointSnapshot))); + } + @Test - @SuppressWarnings({"removal"}) public void testSubjectDoAs() throws Exception { - final String user = "joe"; final Subject subject = new Subject(); subject.getPrincipals().add(() -> user); @@ -65,61 +81,61 @@ public Result authenticate(HttpExchange exchange) { .authenticatedSubjectAttributeName("aa") .buildAndStart(); - run(server, "204", "/"); + run(server, "/", 204, ""); } - private static void run(HTTPServer server, String expected, String path) throws IOException { - try (Socket socket = new Socket()) { - socket.connect(new InetSocketAddress("localhost", server.getPort())); - - socket - .getOutputStream() - .write(("GET " + path + " HTTP/1.1 \r\n").getBytes(StandardCharsets.UTF_8)); - socket.getOutputStream().write("HOST: localhost \r\n\r\n".getBytes(StandardCharsets.UTF_8)); - socket.getOutputStream().flush(); - - String actualResponse = ""; - byte[] resp = new byte[500]; - int read = socket.getInputStream().read(resp, 0, resp.length); - if (read > 0) { - actualResponse = new String(resp, 0, read, StandardCharsets.UTF_8); - } - assertThat(actualResponse).contains(expected); - } + @Test + void defaultHandler() throws Exception { + run( + HTTPServer.builder().port(0).buildAndStart(), + "/", + 200, + "Prometheus Java Client"); } @Test - void defaultHandler() throws IOException { - run(HTTPServer.builder().port(0).buildAndStart(), "200", "/"); + void metrics() throws Exception { + run( + HTTPServer.builder() + .port(0) + .registry(registry) + .executorService(Executors.newFixedThreadPool(1)) + .buildAndStart(), + "/metrics", + 200, + "my_counter_total 1.0"); } @Test - void metrics() throws IOException { + void metricsCustomPath() throws Exception { run( HTTPServer.builder() .port(0) - .registry(new PrometheusRegistry()) + .registry(registry) + .metricsHandlerPath("/my-metrics") .executorService(Executors.newFixedThreadPool(1)) .buildAndStart(), - "200", - "/metrics"); + "/my-metrics", + 200, + "my_counter_total 1.0"); } @Test - void metricsCustomPath() throws IOException { + void metricsCustomRootPath() throws Exception { run( HTTPServer.builder() .port(0) - .registry(new PrometheusRegistry()) - .metricsHandlerPath("/my-metrics") + .registry(registry) + .metricsHandlerPath("/") .executorService(Executors.newFixedThreadPool(1)) .buildAndStart(), - "200", - "/my-metrics"); + "/", + 200, + "my_counter_total 1.0"); } @Test - void registryThrows() throws IOException { + void registryThrows() throws Exception { HTTPServer server = HTTPServer.builder() .port(0) @@ -131,11 +147,12 @@ public MetricSnapshots scrape(PrometheusScrapeRequest scrapeRequest) { } }) .buildAndStart(); - run(server, "500", "/metrics"); + run(server, "/metrics", 500, "An Exception occurred while scraping metrics"); } @Test - void config() throws NoSuchAlgorithmException, IOException { + @SuppressWarnings("resource") + void config() { assertThatExceptionOfType(IllegalStateException.class) .isThrownBy( () -> @@ -146,23 +163,27 @@ void config() throws NoSuchAlgorithmException, IOException { .buildAndStart()) .withMessage("cannot configure 'inetAddress' and 'hostname' at the same time"); - // ssl doesn't work without in tests - run( - HTTPServer.builder() - .port(0) - .httpsConfigurator(new HttpsConfigurator(SSLContext.getDefault())) - .buildAndStart(), - "", - "/"); + // SSL doesn't work in this simple test configuration + assertThatExceptionOfType(IOException.class) + .isThrownBy( + () -> + run( + HTTPServer.builder() + .port(0) + .httpsConfigurator(new HttpsConfigurator(SSLContext.getDefault())) + .buildAndStart(), + "/", + 0, + "ignored")); } @Test - void health() throws IOException { - run(HTTPServer.builder().port(0).buildAndStart(), "200", "/-/healthy"); + void health() throws Exception { + run(HTTPServer.builder().port(0).buildAndStart(), "/-/healthy", 200, "Exporter is healthy."); } @Test - void healthEnabled() throws IOException { + void healthEnabled() throws Exception { HttpHandler handler = exchange -> exchange.sendResponseHeaders(204, -1); run( HTTPServer.builder() @@ -170,12 +191,13 @@ void healthEnabled() throws IOException { .defaultHandler(handler) .registerHealthHandler(true) .buildAndStart(), - "200", - "/-/healthy"); + "/-/healthy", + 200, + "Exporter is healthy."); } @Test - void healthDisabled() throws IOException { + void healthDisabled() throws Exception { HttpHandler handler = exchange -> exchange.sendResponseHeaders(204, -1); run( HTTPServer.builder() @@ -183,7 +205,24 @@ void healthDisabled() throws IOException { .defaultHandler(handler) .registerHealthHandler(false) .buildAndStart(), - "204", - "/-/healthy"); + "/-/healthy", + 204, + ""); + } + + private static void run( + HTTPServer server, String path, int expectedStatusCode, String expectedBody) + throws Exception { + try (final HttpClient client = HttpClient.newBuilder().build()) { + final URI uri = URI.create("http://localhost:%s%s".formatted(server.getPort(), path)); + final HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build(); + + final HttpResponse response = + client.send(request, HttpResponse.BodyHandlers.ofString()); + assertThat(response.statusCode()).isEqualTo(expectedStatusCode); + assertThat(response.body()).contains(expectedBody); + } finally { + server.stop(); + } } }