lib/opentelemetry/exporter/otlp/exporter.rb in opentelemetry-exporter-otlp-0.20.3 vs lib/opentelemetry/exporter/otlp/exporter.rb in opentelemetry-exporter-otlp-0.20.4

- old
+ new

@@ -29,30 +29,32 @@ KEEP_ALIVE_TIMEOUT = 30 RETRY_COUNT = 5 WRITE_TIMEOUT_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6') private_constant(:KEEP_ALIVE_TIMEOUT, :RETRY_COUNT, :WRITE_TIMEOUT_SUPPORTED) + ERROR_MESSAGE_INVALID_HEADERS = 'headers must be a String with comma-separated URL Encoded UTF-8 k=v pairs or a Hash' + private_constant(:ERROR_MESSAGE_INVALID_HEADERS) + def self.ssl_verify_mode if ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER') OpenSSL::SSL::VERIFY_PEER elsif ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE') OpenSSL::SSL::VERIFY_NONE else OpenSSL::SSL::VERIFY_PEER end end - def initialize(endpoint: config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'https://localhost:4317/v1/traces'), # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength + def initialize(endpoint: config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'https://localhost:4317/v1/traces'), # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity certificate_file: config_opt('OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), ssl_verify_mode: Exporter.ssl_verify_mode, - headers: config_opt('OTEL_EXPORTER_OTLP_TRACES_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS'), + headers: config_opt('OTEL_EXPORTER_OTLP_TRACES_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS', default: {}), compression: config_opt('OTEL_EXPORTER_OTLP_TRACES_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION'), timeout: config_opt('OTEL_EXPORTER_OTLP_TRACES_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10), metrics_reporter: nil) raise ArgumentError, "invalid url for OTLP::Exporter #{endpoint}" if invalid_url?(endpoint) raise ArgumentError, "unsupported compression key #{compression}" unless compression.nil? || compression == 'gzip' - raise ArgumentError, 'headers must be comma-separated k=v pairs or a Hash' unless valid_headers?(headers) @uri = if endpoint == ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] URI("#{endpoint}/v1/traces") else URI(endpoint) @@ -64,12 +66,14 @@ @http.ca_file = certificate_file unless certificate_file.nil? @http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT @path = @uri.path @headers = case headers - when String then CSV.parse(headers, col_sep: '=', row_sep: ',').to_h + when String then parse_headers(headers) when Hash then headers + else + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS end @timeout = timeout.to_f @compression = compression @metrics_reporter = metrics_reporter || OpenTelemetry::SDK::Trace::Export::MetricsReporter @shutdown = false @@ -116,20 +120,10 @@ return val unless val.nil? end default end - def valid_headers?(headers) - return true if headers.nil? || headers.is_a?(Hash) - return false unless headers.is_a?(String) - - CSV.parse(headers, col_sep: '=', row_sep: ',').to_h - true - rescue ArgumentError - false - end - def invalid_url?(url) return true if url.nil? || url.strip.empty? URI(url) false @@ -159,11 +153,11 @@ Zlib.gzip(bytes) else bytes end request.add_field('Content-Type', 'application/x-protobuf') - @headers&.each { |key, value| request.add_field(key, value) } + @headers.each { |key, value| request.add_field(key, value) } remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time) return TIMEOUT if remaining_timeout.zero? @http.open_timeout = remaining_timeout @@ -364,9 +358,27 @@ when Array values = value.map { |element| as_otlp_any_value(element) } result.array_value = Opentelemetry::Proto::Common::V1::ArrayValue.new(values: values) end result + end + + def parse_headers(raw) + entries = raw.split(',') + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if entries.empty? + + entries.each_with_object({}) do |entry, headers| + k, v = entry.split('=', 2).map(&CGI.method(:unescape)) + begin + k = k.to_s.strip + v = v.to_s.strip + rescue ArgumentError => e + raise e, ERROR_MESSAGE_INVALID_HEADERS + end + raise ArgumentError, ERROR_MESSAGE_INVALID_HEADERS if k.empty? || v.empty? + + headers[k] = v + end end end end end end