From 3bd75f6ce5ab9dd01e7ad2a7b2fe055fd5f404c5 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 13 Mar 2025 11:26:23 +0100 Subject: [PATCH] Revert back to the custom decode method for Otel decoding https://github.com/spring-projects/spring-framework/issues/34570 See gh-44677 --- .../OpenTelemetryResourceAttributes.java | 42 ++++++++++++++++++- .../OpenTelemetryResourceAttributesTests.java | 4 +- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java index 4e94ed213f..f68d14f3fd 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributes.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.opentelemetry; +import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; @@ -138,7 +139,7 @@ public final class OpenTelemetryResourceAttributes { if (index > 0) { String key = attribute.substring(0, index); String value = attribute.substring(index + 1); - attributes.put(key.trim(), StringUtils.uriDecode(value.trim(), StandardCharsets.UTF_8)); + attributes.put(key.trim(), decode(value.trim())); } } String otelServiceName = getEnv("OTEL_SERVICE_NAME"); @@ -152,4 +153,43 @@ public final class OpenTelemetryResourceAttributes { return this.getEnv.apply(name); } + /** + * Decodes a percent-encoded string. Converts sequences like '%HH' (where HH + * represents hexadecimal digits) back into their literal representations. + *

+ * Inspired by {@code org.apache.commons.codec.net.PercentCodec}. + * @param value value to decode + * @return the decoded string + */ + private static String decode(String value) { + if (value.indexOf('%') < 0) { + return value; + } + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length); + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + if (b != '%') { + bos.write(b); + continue; + } + int u = decodeHex(bytes, i + 1); + int l = decodeHex(bytes, i + 2); + if (u >= 0 && l >= 0) { + bos.write((u << 4) + l); + } + else { + throw new IllegalArgumentException( + "Failed to decode percent-encoded characters at index %d in the value: '%s'".formatted(i, + value)); + } + i += 2; + } + return bos.toString(StandardCharsets.UTF_8); + } + + private static int decodeHex(byte[] bytes, int index) { + return (index < bytes.length) ? Character.digit(bytes[index], 16) : -1; + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java index 799c7175de..4b6f139ee3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryResourceAttributesTests.java @@ -121,7 +121,7 @@ class OpenTelemetryResourceAttributesTests { void illegalArgumentExceptionShouldBeThrownWhenDecodingIllegalHexCharPercentEncodedValue() { this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "key=abc%ß"); assertThatIllegalArgumentException().isThrownBy(this::getAttributes) - .withMessage("Invalid encoded sequence \"%ß\""); + .withMessage("Failed to decode percent-encoded characters at index 3 in the value: 'abc%ß'"); } @Test @@ -134,7 +134,7 @@ class OpenTelemetryResourceAttributesTests { void illegalArgumentExceptionShouldBeThrownWhenDecodingInvalidPercentEncodedValue() { this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "key=%"); assertThatIllegalArgumentException().isThrownBy(this::getAttributes) - .withMessage("Invalid encoded sequence \"%\""); + .withMessage("Failed to decode percent-encoded characters at index 0 in the value: '%'"); } @Test