Line data Source code
1 : #include "source/extensions/tracers/opentelemetry/span_context_extractor.h" 2 : 3 : #include "envoy/tracing/tracer.h" 4 : 5 : #include "source/common/http/header_map_impl.h" 6 : #include "source/common/tracing/trace_context_impl.h" 7 : 8 : #include "absl/strings/escaping.h" 9 : #include "span_context.h" 10 : 11 : namespace Envoy { 12 : namespace Extensions { 13 : namespace Tracers { 14 : namespace OpenTelemetry { 15 : namespace { 16 : 17 : // See https://www.w3.org/TR/trace-context/#traceparent-header 18 : constexpr int kTraceparentHeaderSize = 55; // 2 + 1 + 32 + 1 + 16 + 1 + 2 19 : constexpr int kVersionHexSize = 2; 20 : constexpr int kTraceIdHexSize = 32; 21 : constexpr int kParentIdHexSize = 16; 22 : constexpr int kTraceFlagsHexSize = 2; 23 : 24 0 : bool isValidHex(const absl::string_view& input) { 25 0 : return std::all_of(input.begin(), input.end(), 26 0 : [](const char& c) { return absl::ascii_isxdigit(c); }); 27 0 : } 28 : 29 0 : bool isAllZeros(const absl::string_view& input) { 30 0 : return std::all_of(input.begin(), input.end(), [](const char& c) { return c == '0'; }); 31 0 : } 32 : 33 : } // namespace 34 : 35 : SpanContextExtractor::SpanContextExtractor(Tracing::TraceContext& trace_context) 36 0 : : trace_context_(trace_context) {} 37 : 38 0 : SpanContextExtractor::~SpanContextExtractor() = default; 39 : 40 0 : bool SpanContextExtractor::propagationHeaderPresent() { 41 0 : auto propagation_header = OpenTelemetryConstants::get().TRACE_PARENT.get(trace_context_); 42 0 : return propagation_header.has_value(); 43 0 : } 44 : 45 0 : absl::StatusOr<SpanContext> SpanContextExtractor::extractSpanContext() { 46 0 : auto propagation_header = OpenTelemetryConstants::get().TRACE_PARENT.get(trace_context_); 47 0 : if (!propagation_header.has_value()) { 48 : // We should have already caught this, but just in case. 49 0 : return absl::InvalidArgumentError("No propagation header found"); 50 0 : } 51 0 : auto header_value_string = propagation_header.value(); 52 : 53 0 : if (header_value_string.size() != kTraceparentHeaderSize) { 54 0 : return absl::InvalidArgumentError("Invalid traceparent header length"); 55 0 : } 56 : // Try to split it into its component parts: 57 0 : std::vector<absl::string_view> propagation_header_components = 58 0 : absl::StrSplit(header_value_string, '-', absl::SkipEmpty()); 59 0 : if (propagation_header_components.size() != 4) { 60 0 : return absl::InvalidArgumentError("Invalid traceparent hyphenation"); 61 0 : } 62 0 : absl::string_view version = propagation_header_components[0]; 63 0 : absl::string_view trace_id = propagation_header_components[1]; 64 0 : absl::string_view parent_id = propagation_header_components[2]; 65 0 : absl::string_view trace_flags = propagation_header_components[3]; 66 0 : if (version.size() != kVersionHexSize || trace_id.size() != kTraceIdHexSize || 67 0 : parent_id.size() != kParentIdHexSize || trace_flags.size() != kTraceFlagsHexSize) { 68 0 : return absl::InvalidArgumentError("Invalid traceparent field sizes"); 69 0 : } 70 0 : if (!isValidHex(version) || !isValidHex(trace_id) || !isValidHex(parent_id) || 71 0 : !isValidHex(trace_flags)) { 72 0 : return absl::InvalidArgumentError("Invalid header hex"); 73 0 : } 74 : // As per the traceparent header definition, if the trace-id or parent-id are all zeros, they are 75 : // invalid and must be ignored. 76 0 : if (isAllZeros(trace_id)) { 77 0 : return absl::InvalidArgumentError("Invalid trace id"); 78 0 : } 79 0 : if (isAllZeros(parent_id)) { 80 0 : return absl::InvalidArgumentError("Invalid parent id"); 81 0 : } 82 : 83 : // Set whether or not the span is sampled from the trace flags. 84 : // See https://w3c.github.io/trace-context/#trace-flags. 85 0 : char decoded_trace_flags = absl::HexStringToBytes(trace_flags).front(); 86 0 : bool sampled = (decoded_trace_flags & 1); 87 : 88 : // If a tracestate header is received without an accompanying traceparent header, 89 : // it is invalid and MUST be discarded. Because we're already checking for the 90 : // traceparent header above, we don't need to check here. 91 : // See https://www.w3.org/TR/trace-context/#processing-model-for-working-with-trace-context 92 0 : absl::string_view tracestate_key = OpenTelemetryConstants::get().TRACE_STATE.key(); 93 0 : std::vector<std::string> tracestate_values; 94 : // Multiple tracestate header fields MUST be handled as specified by RFC7230 Section 3.2.2 Field 95 : // Order. 96 0 : trace_context_.forEach( 97 0 : [&tracestate_key, &tracestate_values](absl::string_view key, absl::string_view value) { 98 0 : if (key == tracestate_key) { 99 0 : tracestate_values.push_back(std::string{value}); 100 0 : } 101 0 : return true; 102 0 : }); 103 0 : std::string tracestate = absl::StrJoin(tracestate_values, ","); 104 : 105 0 : SpanContext span_context(version, trace_id, parent_id, sampled, tracestate); 106 0 : return span_context; 107 0 : } 108 : 109 : } // namespace OpenTelemetry 110 : } // namespace Tracers 111 : } // namespace Extensions 112 : } // namespace Envoy