Skip to content

Commit c378f22

Browse files
committed
fix: prevent exception when using custom root metrics path
Signed-off-by: David Sondermann <[email protected]>
1 parent 9592f50 commit c378f22

File tree

2 files changed

+101
-60
lines changed
  • prometheus-metrics-exporter-httpserver/src

2 files changed

+101
-60
lines changed

prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HTTPServer.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,13 @@ private HTTPServer(
6666
this.server = httpServer;
6767
this.executorService = executorService;
6868
String metricsPath = getMetricsPath(metricsHandlerPath);
69-
registerHandler(
70-
"/",
71-
defaultHandler == null ? new DefaultHandler(metricsPath) : defaultHandler,
72-
authenticator,
73-
authenticatedSubjectAttributeName);
69+
if (!metricsPath.equals("/")) {
70+
registerHandler(
71+
"/",
72+
defaultHandler == null ? new DefaultHandler(metricsPath) : defaultHandler,
73+
authenticator,
74+
authenticatedSubjectAttributeName);
75+
}
7476
registerHandler(
7577
metricsPath,
7678
new MetricsHandler(config, registry),

prometheus-metrics-exporter-httpserver/src/test/java/io/prometheus/metrics/exporter/httpserver/HTTPServerTest.java

Lines changed: 94 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,41 @@
1010
import com.sun.net.httpserver.HttpsConfigurator;
1111
import io.prometheus.metrics.model.registry.PrometheusRegistry;
1212
import io.prometheus.metrics.model.registry.PrometheusScrapeRequest;
13+
import io.prometheus.metrics.model.snapshots.CounterSnapshot;
14+
import io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot;
15+
import io.prometheus.metrics.model.snapshots.Labels;
16+
import io.prometheus.metrics.model.snapshots.MetricMetadata;
1317
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
1418
import java.io.IOException;
1519
import java.net.InetAddress;
16-
import java.net.InetSocketAddress;
17-
import java.net.Socket;
18-
import java.nio.charset.StandardCharsets;
19-
import java.security.NoSuchAlgorithmException;
20+
import java.net.URI;
21+
import java.net.http.HttpClient;
22+
import java.net.http.HttpRequest;
23+
import java.net.http.HttpResponse;
2024
import java.security.Principal;
25+
import java.util.List;
2126
import java.util.concurrent.Executors;
2227
import javax.net.ssl.SSLContext;
2328
import javax.security.auth.Subject;
29+
import org.junit.jupiter.api.BeforeEach;
2430
import org.junit.jupiter.api.Test;
2531

2632
public class HTTPServerTest {
2733

34+
private PrometheusRegistry registry;
35+
36+
@BeforeEach
37+
void setUp() {
38+
final MetricMetadata metadata = new MetricMetadata("my-counter");
39+
final CounterDataPointSnapshot dataPointSnapshot =
40+
new CounterDataPointSnapshot(1.0, Labels.EMPTY, null, System.currentTimeMillis());
41+
42+
registry = new PrometheusRegistry();
43+
registry.register(() -> new CounterSnapshot(metadata, List.of(dataPointSnapshot)));
44+
}
45+
2846
@Test
29-
@SuppressWarnings({"removal"})
3047
public void testSubjectDoAs() throws Exception {
31-
3248
final String user = "joe";
3349
final Subject subject = new Subject();
3450
subject.getPrincipals().add(() -> user);
@@ -65,61 +81,61 @@ public Result authenticate(HttpExchange exchange) {
6581
.authenticatedSubjectAttributeName("aa")
6682
.buildAndStart();
6783

68-
run(server, "204", "/");
84+
run(server, "/", 204, "");
6985
}
7086

71-
private static void run(HTTPServer server, String expected, String path) throws IOException {
72-
try (Socket socket = new Socket()) {
73-
socket.connect(new InetSocketAddress("localhost", server.getPort()));
74-
75-
socket
76-
.getOutputStream()
77-
.write(("GET " + path + " HTTP/1.1 \r\n").getBytes(StandardCharsets.UTF_8));
78-
socket.getOutputStream().write("HOST: localhost \r\n\r\n".getBytes(StandardCharsets.UTF_8));
79-
socket.getOutputStream().flush();
80-
81-
String actualResponse = "";
82-
byte[] resp = new byte[500];
83-
int read = socket.getInputStream().read(resp, 0, resp.length);
84-
if (read > 0) {
85-
actualResponse = new String(resp, 0, read, StandardCharsets.UTF_8);
86-
}
87-
assertThat(actualResponse).contains(expected);
88-
}
87+
@Test
88+
void defaultHandler() throws Exception {
89+
run(
90+
HTTPServer.builder().port(0).buildAndStart(),
91+
"/",
92+
200,
93+
"<title>Prometheus Java Client</title>");
8994
}
9095

9196
@Test
92-
void defaultHandler() throws IOException {
93-
run(HTTPServer.builder().port(0).buildAndStart(), "200", "/");
97+
void metrics() throws Exception {
98+
run(
99+
HTTPServer.builder()
100+
.port(0)
101+
.registry(registry)
102+
.executorService(Executors.newFixedThreadPool(1))
103+
.buildAndStart(),
104+
"/metrics",
105+
200,
106+
"my_counter_total 1.0");
94107
}
95108

96109
@Test
97-
void metrics() throws IOException {
110+
void metricsCustomPath() throws Exception {
98111
run(
99112
HTTPServer.builder()
100113
.port(0)
101-
.registry(new PrometheusRegistry())
114+
.registry(registry)
115+
.metricsHandlerPath("/my-metrics")
102116
.executorService(Executors.newFixedThreadPool(1))
103117
.buildAndStart(),
104-
"200",
105-
"/metrics");
118+
"/my-metrics",
119+
200,
120+
"my_counter_total 1.0");
106121
}
107122

108123
@Test
109-
void metricsCustomPath() throws IOException {
124+
void metricsCustomRootPath() throws Exception {
110125
run(
111126
HTTPServer.builder()
112127
.port(0)
113-
.registry(new PrometheusRegistry())
114-
.metricsHandlerPath("/my-metrics")
128+
.registry(registry)
129+
.metricsHandlerPath("/")
115130
.executorService(Executors.newFixedThreadPool(1))
116131
.buildAndStart(),
117-
"200",
118-
"/my-metrics");
132+
"/",
133+
200,
134+
"my_counter_total 1.0");
119135
}
120136

121137
@Test
122-
void registryThrows() throws IOException {
138+
void registryThrows() throws Exception {
123139
HTTPServer server =
124140
HTTPServer.builder()
125141
.port(0)
@@ -131,11 +147,12 @@ public MetricSnapshots scrape(PrometheusScrapeRequest scrapeRequest) {
131147
}
132148
})
133149
.buildAndStart();
134-
run(server, "500", "/metrics");
150+
run(server, "/metrics", 500, "An Exception occurred while scraping metrics");
135151
}
136152

137153
@Test
138-
void config() throws NoSuchAlgorithmException, IOException {
154+
@SuppressWarnings("resource")
155+
void config() {
139156
assertThatExceptionOfType(IllegalStateException.class)
140157
.isThrownBy(
141158
() ->
@@ -146,44 +163,66 @@ void config() throws NoSuchAlgorithmException, IOException {
146163
.buildAndStart())
147164
.withMessage("cannot configure 'inetAddress' and 'hostname' at the same time");
148165

149-
// ssl doesn't work without in tests
150-
run(
151-
HTTPServer.builder()
152-
.port(0)
153-
.httpsConfigurator(new HttpsConfigurator(SSLContext.getDefault()))
154-
.buildAndStart(),
155-
"",
156-
"/");
166+
// SSL doesn't work in this simple test configuration
167+
assertThatExceptionOfType(IOException.class)
168+
.isThrownBy(
169+
() ->
170+
run(
171+
HTTPServer.builder()
172+
.port(0)
173+
.httpsConfigurator(new HttpsConfigurator(SSLContext.getDefault()))
174+
.buildAndStart(),
175+
"/",
176+
0,
177+
"ignored"));
157178
}
158179

159180
@Test
160-
void health() throws IOException {
161-
run(HTTPServer.builder().port(0).buildAndStart(), "200", "/-/healthy");
181+
void health() throws Exception {
182+
run(HTTPServer.builder().port(0).buildAndStart(), "/-/healthy", 200, "Exporter is healthy.");
162183
}
163184

164185
@Test
165-
void healthEnabled() throws IOException {
186+
void healthEnabled() throws Exception {
166187
HttpHandler handler = exchange -> exchange.sendResponseHeaders(204, -1);
167188
run(
168189
HTTPServer.builder()
169190
.port(0)
170191
.defaultHandler(handler)
171192
.registerHealthHandler(true)
172193
.buildAndStart(),
173-
"200",
174-
"/-/healthy");
194+
"/-/healthy",
195+
200,
196+
"Exporter is healthy.");
175197
}
176198

177199
@Test
178-
void healthDisabled() throws IOException {
200+
void healthDisabled() throws Exception {
179201
HttpHandler handler = exchange -> exchange.sendResponseHeaders(204, -1);
180202
run(
181203
HTTPServer.builder()
182204
.port(0)
183205
.defaultHandler(handler)
184206
.registerHealthHandler(false)
185207
.buildAndStart(),
186-
"204",
187-
"/-/healthy");
208+
"/-/healthy",
209+
204,
210+
"");
211+
}
212+
213+
private static void run(
214+
HTTPServer server, String path, int expectedStatusCode, String expectedBody)
215+
throws Exception {
216+
try (final HttpClient client = HttpClient.newBuilder().build()) {
217+
final URI uri = URI.create("http://localhost:%s%s".formatted(server.getPort(), path));
218+
final HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
219+
220+
final HttpResponse<String> response =
221+
client.send(request, HttpResponse.BodyHandlers.ofString());
222+
assertThat(response.statusCode()).isEqualTo(expectedStatusCode);
223+
assertThat(response.body()).contains(expectedBody);
224+
} finally {
225+
server.stop();
226+
}
188227
}
189228
}

0 commit comments

Comments
 (0)