diff --git a/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java b/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java index b7dd844ec3..5c5cef3092 100644 --- a/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java +++ b/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java @@ -508,7 +508,7 @@ private URL makeUrl(@Nullable ExporterPushgatewayProperties properties) StringBuilder url = new StringBuilder(getScheme(properties) + "://" + getAddress(properties) + "/metrics/"); String job = getJob(properties); - if (job.contains("/")) { + if (needsBase64(job)) { url.append("job@base64/").append(base64url(job)); } else { url.append("job/").append(URLEncoder.encode(job, "UTF-8")); @@ -518,7 +518,7 @@ private URL makeUrl(@Nullable ExporterPushgatewayProperties properties) url.append("/") .append(escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING)) .append("@base64/="); - } else if (entry.getValue().contains("/")) { + } else if (needsBase64(entry.getValue())) { url.append("/") .append(escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING)) .append("@base64/") @@ -533,6 +533,12 @@ private URL makeUrl(@Nullable ExporterPushgatewayProperties properties) return URI.create(url.toString()).normalize().toURL(); } + // "/" would split the value into several path segments, and "." and ".." are dot segments + // that normalize() would remove from the path, sending the request to a different group. + private boolean needsBase64(String v) { + return v.contains("/") || v.equals(".") || v.equals(".."); + } + private String base64url(String v) { return Base64.getEncoder() .encodeToString(v.getBytes(StandardCharsets.UTF_8)) diff --git a/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java b/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java index 8c33543f95..a2296db7c0 100644 --- a/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java +++ b/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java @@ -232,6 +232,25 @@ void testPushWithGroupingKeyWithSlashes() throws IOException { pg.push(); } + @Test + void testPushWithDotSegmentJobAndGroupingKey() throws IOException { + mockServerClient + .when( + request() + .withMethod("PUT") + .withPath("/metrics/job@base64/Li4=/l@base64/Lg==/l2@base64/Li4=")) + .respond(response().withStatusCode(202)); + PushGateway pg = + PushGateway.builder() + .address("localhost:" + mockServerClient.getPort()) + .registry(registry) + .job("..") + .groupingKey("l", ".") + .groupingKey("l2", "..") + .build(); + pg.push(); + } + @Test void testPushCollectorWithGroupingKey() throws IOException { mockServerClient