Skip to content

Commit

Permalink
header values are be expected to be W3C baggage encoded
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitlinger committed Jan 19, 2024
1 parent 9e208c8 commit 713acfd
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;

import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.AbstractMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
Expand Down Expand Up @@ -79,10 +82,21 @@ static <G, H, E> E applySignalProperties(
headers = properties.getHeaders();
}
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (isHttpProtobuf) {
addHttpHeader.accept(httpBuilder, entry);
} else {
addGrpcHeader.accept(grpcBuilder, entry);
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
Map.Entry<String, String> decoded =
new AbstractMap.SimpleEntry<>(
entry.getKey(), URLDecoder.decode(value, StandardCharsets.UTF_8.displayName()));

if (isHttpProtobuf) {
addHttpHeader.accept(httpBuilder, decoded);
} else {
addGrpcHeader.accept(grpcBuilder, decoded);
}
} catch (Exception e) {
throw new IllegalArgumentException("Cannot decode header value: " + value, e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
import java.util.stream.Collectors;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;

/** Spring Boot auto configuration test for {@link OtlpSpanExporterAutoConfiguration}. */
@ExtendWith(OutputCaptureExtension.class)
class OtlpSpanExporterAutoConfigurationTest {

private final OtlpHttpSpanExporterBuilder otlpHttpSpanExporterBuilder =
Expand Down Expand Up @@ -118,15 +122,27 @@ void useHttpWithEnv() {
.withPropertyValues(
"otel.exporter.otlp.enabled=true", "otel.exporter.otlp.protocol=http/protobuf")
// are similar to environment variables in that they use the same converters
.withSystemProperties("otel.exporter.otlp.headers=x=1,y=2")
.withSystemProperties("otel.exporter.otlp.headers=x=1,y=2%203")
.run(context -> {});

Mockito.verify(otlpHttpSpanExporterBuilder).build();
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("x", "1");
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("y", "2");
Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("y", "2 3");
Mockito.verifyNoMoreInteractions(otlpHttpSpanExporterBuilder);
}

@Test
@DisplayName("OTLP header with illegal % encoding causes an exception")
void decodingError(CapturedOutput output) {
this.contextRunner
.withBean(OtlpHttpSpanExporterBuilder.class, () -> otlpHttpSpanExporterBuilder)
.withSystemProperties("otel.exporter.otlp.headers=x=%-1")
.run(context -> {});

// spring catches the exception and logs it
assertThat(output).contains("Cannot decode header value: %-1");
}

@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
Expand Down

0 comments on commit 713acfd

Please sign in to comment.