From ecbd1f9d3d8d131588dec983c1fe8474bb3808e1 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 1 Feb 2024 22:00:56 +0100 Subject: [PATCH] header values are be expected to be W3C baggage encoded (#6164) Co-authored-by: jack-berg <34418638+jack-berg@users.noreply.github.com> Co-authored-by: Jack Berg --- .../otlp/internal/OtlpConfigUtil.java | 14 +++++++++++++- .../OtlpSpanExporterProviderTest.java | 19 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java index 1602cdd246b..da02c32dee5 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java @@ -21,6 +21,8 @@ import java.io.RandomAccessFile; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Locale; import java.util.Map; @@ -89,7 +91,17 @@ public static void configureOtlpExporterBuilder( if (headers.isEmpty()) { headers = config.getMap("otel.exporter.otlp.headers"); } - headers.forEach(addHeader); + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + try { + // headers are encoded as URL - see + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables + addHeader.accept(key, URLDecoder.decode(value, StandardCharsets.UTF_8.displayName())); + } catch (Exception e) { + throw new ConfigurationException("Cannot decode header value: " + value, e); + } + } String compression = config.getString("otel.exporter.otlp." + dataType + ".compression"); if (compression == null) { diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java index 7d83c3104f8..1918408e75b 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -219,7 +220,8 @@ void createExporter_HttpWithGeneralConfiguration() throws CertificateEncodingExc config.put("otel.exporter.otlp.certificate", certificatePath); config.put("otel.exporter.otlp.client.key", clientKeyPath); config.put("otel.exporter.otlp.client.certificate", clientCertificatePath); - config.put("otel.exporter.otlp.headers", "header-key=header-value"); + config.put( + "otel.exporter.otlp.headers", "header-key1=header%20value1,header-key2=header value2"); config.put("otel.exporter.otlp.compression", "gzip"); config.put("otel.exporter.otlp.timeout", "15s"); config.put("otel.experimental.exporter.otlp.retry.enabled", "true"); @@ -229,7 +231,8 @@ void createExporter_HttpWithGeneralConfiguration() throws CertificateEncodingExc assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class); verify(httpBuilder, times(1)).build(); verify(httpBuilder).setEndpoint("https://localhost:443/v1/traces"); - verify(httpBuilder).addHeader("header-key", "header-value"); + verify(httpBuilder).addHeader("header-key1", "header value1"); + verify(httpBuilder).addHeader("header-key2", "header value2"); verify(httpBuilder).setCompression("gzip"); verify(httpBuilder).setTimeout(Duration.ofSeconds(15)); verify(httpBuilder).setTrustedCertificates(serverTls.certificate().getEncoded()); @@ -274,4 +277,16 @@ void createExporter_HttpWithSignalConfiguration() throws CertificateEncodingExce } Mockito.verifyNoInteractions(grpcBuilder); } + + @Test + void createExporter_decodingError() { + Assertions.assertThatThrownBy( + () -> { + provider.createExporter( + DefaultConfigProperties.createFromMap( + Collections.singletonMap("otel.exporter.otlp.headers", "header-key=%-1"))); + }) + .isInstanceOf(ConfigurationException.class) + .hasMessage("Cannot decode header value: %-1"); + } }