Describe the bug
W3CBaggagePropagator uses java.net.URLDecoder to decode baggage values which may be percent-encoded as per https://w3c.github.io/baggage/#definition
Looking at that specification, the + character is a valid character. However, URLDecoder transforms it to a space character. In particular, this change corrupts baggage values that are Base64 encoded.
Steps to reproduce
package org.example;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
public class Main {
public static void main(String[] args) {
TextMapGetter<Map<String, String>> getter = new TextMapGetter<>() {
@Override
public Iterable<String> keys(Map<String, String> carrier) {
return carrier.keySet();
}
@Override
public String get(Map<String, String> carrier, String key) {
return carrier.get(key);
}
};
String encodedInBase64 = "MngJIFNQIAkhCSIJIwkkCSUJJgknCSgJKQkqCSsJLAktCS4JLwozeAkwCTEJMgkzCTQJNQk2CTcJOAk5CToJOwk8CT0JPgk/CjR4CUAJQQlCCUMJRAlFCUYJRwlICUkJSglLCUwJTQlOCU8KNXgJUAlRCVIJUwlUCVUJVglXCVgJWQlaCVsJXAldCV4JXwo2eAlgCWEJYgljCWQJZQlmCWcJaAlpCWoJawlsCW0JbglvCjd4CXAJcQlyCXMJdAl1CXYJdwl4CXkJegl7CXwJfQl+CQo4eAkJCQkJCQkJCQkJCQkJCQkKOXgJCQkJCQkJCQkJCQkJCQkJCkF4CU5CU1AJwqEJwqIJwqMJwqQJwqUJwqYJwqcJwqgJwqkJwqoJwqsJwqwJU0hZCcKuCcKvCkJ4CcKwCcKxCcKyCcKzCcK0CcK1CcK2CcK3CcK4CcK5CcK6CcK7CcK8CcK9CcK+CcK/CkN4CcOACcOBCcOCCcODCcOECcOFCcOGCcOHCcOICcOJCcOKCcOLCcOMCcONCcOOCcOPCkR4CcOQCcORCcOSCcOTCcOUCcOVCcOWCcOXCcOYCcOZCcOaCcObCcOcCcOdCcOeCcOfCkV4CcOgCcOhCcOiCcOjCcOkCcOlCcOmCcOnCcOoCcOpCcOqCcOrCcOsCcOtCcOuCcOvCkZ4CcOwCcOxCcOyCcOzCcO0CcO1CcO2CcO3CcO4CcO5CcO6CcO7CcO8CcO9CcO+CcO/Cgo=";
Map<String, String> headers = new HashMap<>();
headers.put("baggage", "foo="+encodedInBase64);
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setPropagators(ContextPropagators.create(W3CBaggagePropagator.getInstance()))
.build();
Context context = Context.current();
Context contextWithBaggage = openTelemetry.getPropagators().getTextMapPropagator().extract(context, headers, getter);
Baggage baggage = Baggage.fromContext(contextWithBaggage);
String fooValue = baggage.getEntryValue("foo");
System.out.println(encodedInBase64);
System.out.println(fooValue);
Base64.getDecoder().decode(fooValue);
}
}
What did you expect to see?
encodedInBase64: MngJIFNQIAkhCSIJIwkkCSUJJgknCSgJKQkqCSsJLAktCS4JLwozeAkwCTEJMgkzCTQJNQk2CTcJOAk5CToJOwk8CT0JPgk/CjR4CUAJQQlCCUMJRAlFCUYJRwlICUkJSglLCUwJTQlOCU8KNXgJUAlRCVIJUwlUCVUJVglXCVgJWQlaCVsJXAldCV4JXwo2eAlgCWEJYgljCWQJZQlmCWcJaAlpCWoJawlsCW0JbglvCjd4CXAJcQlyCXMJdAl1CXYJdwl4CXkJegl7CXwJfQl+CQo4eAkJCQkJCQkJCQkJCQkJCQkKOXgJCQkJCQkJCQkJCQkJCQkJCkF4CU5CU1AJwqEJwqIJwqMJwqQJwqUJwqYJwqcJwqgJwqkJwqoJwqsJwqwJU0hZCcKuCcKvCkJ4CcKwCcKxCcKyCcKzCcK0CcK1CcK2CcK3CcK4CcK5CcK6CcK7CcK8CcK9CcK+CcK/CkN4CcOACcOBCcOCCcODCcOECcOFCcOGCcOHCcOICcOJCcOKCcOLCcOMCcONCcOOCcOPCkR4CcOQCcORCcOSCcOTCcOUCcOVCcOWCcOXCcOYCcOZCcOaCcObCcOcCcOdCcOeCcOfCkV4CcOgCcOhCcOiCcOjCcOkCcOlCcOmCcOnCcOoCcOpCcOqCcOrCcOsCcOtCcOuCcOvCkZ4CcOwCcOxCcOyCcOzCcO0CcO1CcO2CcO3CcO4CcO5CcO6CcO7CcO8CcO9CcO+CcO/Cgo=
fooValue: MngJIFNQIAkhCSIJIwkkCSUJJgknCSgJKQkqCSsJLAktCS4JLwozeAkwCTEJMgkzCTQJNQk2CTcJOAk5CToJOwk8CT0JPgk/CjR4CUAJQQlCCUMJRAlFCUYJRwlICUkJSglLCUwJTQlOCU8KNXgJUAlRCVIJUwlUCVUJVglXCVgJWQlaCVsJXAldCV4JXwo2eAlgCWEJYgljCWQJZQlmCWcJaAlpCWoJawlsCW0JbglvCjd4CXAJcQlyCXMJdAl1CXYJdwl4CXkJegl7CXwJfQl CQo4eAkJCQkJCQkJCQkJCQkJCQkKOXgJCQkJCQkJCQkJCQkJCQkJCkF4CU5CU1AJwqEJwqIJwqMJwqQJwqUJwqYJwqcJwqgJwqkJwqoJwqsJwqwJU0hZCcKuCcKvCkJ4CcKwCcKxCcKyCcKzCcK0CcK1CcK2CcK3CcK4CcK5CcK6CcK7CcK8CcK9CcK CcK/CkN4CcOACcOBCcOCCcODCcOECcOFCcOGCcOHCcOICcOJCcOKCcOLCcOMCcONCcOOCcOPCkR4CcOQCcORCcOSCcOTCcOUCcOVCcOWCcOXCcOYCcOZCcOaCcObCcOcCcOdCcOeCcOfCkV4CcOgCcOhCcOiCcOjCcOkCcOlCcOmCcOnCcOoCcOpCcOqCcOrCcOsCcOtCcOuCcOvCkZ4CcOwCcOxCcOyCcOzCcO0CcO1CcO2CcO3CcO4CcO5CcO6CcO7CcO8CcO9CcO CcO/Cgo=
encodedInBase64 and fooValue should be the same string. Furthermore, Base64.getDecoder().decode(fooValue) should not fail.
What did you see instead?
fooValue as a space where encodedInBase64 has as a + character.
Base64.getDecoder().decode(fooValue); throws a java.lang.IllegalArgumentException: Illegal base64 character 20
What version and what artifacts are you using?
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.19.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
</dependencies>
Environment
Compiler: (e.g., "JDK 18.0.2")
OS: (e.g., "Mac OS Monterey")
Describe the bug
W3CBaggagePropagatorusesjava.net.URLDecoderto decode baggage values which may be percent-encoded as per https://w3c.github.io/baggage/#definitionLooking at that specification, the
+character is a valid character. However,URLDecodertransforms it to a spacecharacter. In particular, this change corrupts baggage values that are Base64 encoded.Steps to reproduce
What did you expect to see?
encodedInBase64andfooValueshould be the same string. Furthermore,Base64.getDecoder().decode(fooValue)should not fail.What did you see instead?
fooValueas a space whereencodedInBase64has as a+character.Base64.getDecoder().decode(fooValue);throws ajava.lang.IllegalArgumentException: Illegal base64 character 20What version and what artifacts are you using?
Environment
Compiler: (e.g., "JDK 18.0.2")
OS: (e.g., "Mac OS Monterey")