diff --git a/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb b/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb index 0197c2f9c3..0f58318465 100644 --- a/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb +++ b/api/lib/opentelemetry/baggage/propagation/text_map_propagator.rb @@ -54,18 +54,12 @@ def inject(carrier, context: Context.current, setter: Context::Propagation.text_ def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter) header = getter.get(carrier, BAGGAGE_KEY) return context if header.nil? || header.empty? + return context if header.bytesize > MAX_TOTAL_LENGTH entries = header.gsub(/\s/, '').split(',') OpenTelemetry::Baggage.build(context: context) do |builder| - entries.each do |entry| - # Note metadata is currently unused in OpenTelemetry, but is part - # the W3C spec where it's referred to as properties. We preserve - # the properties (as-is) so that they can be propagated elsewhere. - kv, meta = entry.split(';', 2) - k, v = kv.split('=').map!(&URI.method(:decode_uri_component)) - builder.set_value(k, v, metadata: meta) - end + decode_entries(entries, builder) end rescue StandardError => e OpenTelemetry.logger.debug "Error extracting W3C baggage: #{e.message}" @@ -82,6 +76,22 @@ def fields private + def decode_entries(entries, builder) + decoded_count = 0 + entries.each do |entry| + break if decoded_count >= MAX_ENTRIES + next if entry.bytesize > MAX_ENTRY_LENGTH + + # Note metadata is currently unused in OpenTelemetry, but is part + # the W3C spec where it's referred to as properties. We preserve + # the properties (as-is) so that they can be propagated elsewhere. + kv, meta = entry.split(';', 2) + k, v = kv.split('=').map!(&URI.method(:decode_uri_component)) + builder.set_value(k, v, metadata: meta) + decoded_count += 1 + end + end + def encode(baggage) result = +'' encoded_count = 0 diff --git a/api/test/opentelemetry/baggage/propagation/text_map_propagator_test.rb b/api/test/opentelemetry/baggage/propagation/text_map_propagator_test.rb index 9d5187cea9..03359b4502 100644 --- a/api/test/opentelemetry/baggage/propagation/text_map_propagator_test.rb +++ b/api/test/opentelemetry/baggage/propagation/text_map_propagator_test.rb @@ -70,6 +70,41 @@ _(context.object_id).wont_equal(empty_context.object_id) end end + + describe 'limits mirroring #inject' do + it 'returns the same context object when the header exceeds the total length of 8192 bytes' do + header = (['k=' + ('v' * 96)] * 100).join(',') + _(header.bytesize).must_be :>, 8192 + carrier = { header_key => header } + empty_context = Context.empty + context = propagator.extract(carrier, context: empty_context) + _(context.object_id).must_equal(empty_context.object_id) + end + + it 'enforces max of 180 entries on extract' do + header = (0..199).map { |i| "k#{i}=v#{i}" }.join(',') + carrier = { header_key => header } + context = propagator.extract(carrier, context: Context.empty) + entries = OpenTelemetry::Baggage.raw_entries(context: context) + _(entries.size).must_equal(180) + _(entries['k0']).wont_be_nil + _(entries['k179']).wont_be_nil + _(entries['k180']).must_be_nil + _(entries['k199']).must_be_nil + end + + it 'skips entries whose size exceeds the max entry length of 4096 bytes' do + oversize_value = 'v' * 4096 + header = "big=#{oversize_value},key2=val2" + _(header.bytesize).must_be :<=, 8192 + carrier = { header_key => header } + context = propagator.extract(carrier, context: Context.empty) + entries = OpenTelemetry::Baggage.raw_entries(context: context) + _(entries['big']).must_be_nil + _(entries['key2']).wont_be_nil + _(entries['key2'].value).must_equal('val2') + end + end end describe '#inject' do