@@ -18,11 +18,23 @@ module XRay
1818 # Propagates context in carriers in the xray single header format
1919 class TextMapPropagator
2020 XRAY_CONTEXT_KEY = 'X-Amzn-Trace-Id'
21- XRAY_CONTEXT_REGEX = /\A Root=(?<trace_id>([a-z0-9\- ]{35}))(?:;Parent=(?<span_id>([a-z0-9]{16})))?(?:;Sampled=(?<sampling_state>[01d](?![0-9a-f])))?(?:;(?<trace_state>.*))?\Z / # rubocop:disable Lint/MixedRegexpCaptureTypes
2221 SAMPLED_VALUES = %w[ 1 d ] . freeze
2322 FIELDS = [ XRAY_CONTEXT_KEY ] . freeze
2423
25- private_constant :XRAY_CONTEXT_KEY , :XRAY_CONTEXT_REGEX , :SAMPLED_VALUES , :FIELDS
24+ # Header parsing constants
25+ KV_PAIR_DELIMITER = ';'
26+ KEY_AND_VALUE_DELIMITER = '='
27+ TRACE_ID_KEY = 'Root'
28+ PARENT_ID_KEY = 'Parent'
29+ SAMPLED_FLAG_KEY = 'Sampled'
30+ TRACE_ID_LENGTH = 35
31+ SPAN_ID_LENGTH = 16
32+ VALID_SAMPLED_VALUES = %w[ 0 1 d ] . freeze
33+
34+ private_constant :XRAY_CONTEXT_KEY , :SAMPLED_VALUES , :FIELDS ,
35+ :KV_PAIR_DELIMITER , :KEY_AND_VALUE_DELIMITER ,
36+ :TRACE_ID_KEY , :PARENT_ID_KEY , :SAMPLED_FLAG_KEY ,
37+ :TRACE_ID_LENGTH , :SPAN_ID_LENGTH , :VALID_SAMPLED_VALUES
2638
2739 # Extract trace context from the supplied carrier.
2840 # If extraction fails, the original context will be returned
@@ -90,11 +102,56 @@ def inject(carrier, context: Context.current, setter: Context::Propagation.text_
90102 private
91103
92104 def parse_header ( header )
93- return nil unless ( match = header . match ( XRAY_CONTEXT_REGEX ) )
94- return nil unless match [ 'trace_id' ]
95- return nil unless match [ 'span_id' ]
105+ trace_id = nil
106+ span_id = nil
107+ sampling_state = nil
108+ trace_state_parts = [ ]
109+
110+ header . split ( KV_PAIR_DELIMITER ) . each do |pair |
111+ # Split only on first '=' to handle values that might contain '='
112+ key , value = pair . split ( KEY_AND_VALUE_DELIMITER , 2 )
113+ next unless key && value
114+
115+ case key
116+ when TRACE_ID_KEY
117+ trace_id = value if valid_trace_id? ( value )
118+ when PARENT_ID_KEY
119+ span_id = value if valid_span_id? ( value )
120+ when SAMPLED_FLAG_KEY
121+ sampling_state = value if valid_sampling_state? ( value )
122+ when 'Self'
123+ # Ignore Self field added by load balancers
124+ next
125+ else
126+ # Collect other fields as potential tracestate
127+ trace_state_parts << pair
128+ end
129+ end
130+
131+ return nil unless trace_id && span_id
132+
133+ {
134+ 'trace_id' => trace_id ,
135+ 'span_id' => span_id ,
136+ 'sampling_state' => sampling_state ,
137+ 'trace_state' => trace_state_parts . empty? ? nil : trace_state_parts . join ( KV_PAIR_DELIMITER )
138+ }
139+ end
140+
141+ def valid_trace_id? ( value )
142+ return false unless value . length == TRACE_ID_LENGTH
143+ return false unless value . start_with? ( '1-' )
144+ return false unless value [ 10 ] == '-'
145+
146+ true
147+ end
148+
149+ def valid_span_id? ( value )
150+ value . length == SPAN_ID_LENGTH && value . match? ( /\A [a-f0-9]+\z / )
151+ end
96152
97- match
153+ def valid_sampling_state? ( value )
154+ VALID_SAMPLED_VALUES . include? ( value )
98155 end
99156
100157 # Convert an id from a hex encoded string to byte array. Assumes the input id has already been
0 commit comments