elastic-transport 8.0.0 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/license.yml +2 -2
- data/.github/workflows/otel.yml +48 -0
- data/.github/workflows/tests.yml +45 -5
- data/.gitignore +1 -1
- data/CHANGELOG.md +131 -8
- data/CONTRIBUTING.md +64 -0
- data/Gemfile +10 -9
- data/Gemfile-faraday1.gemfile +40 -0
- data/README.md +7 -528
- data/Rakefile +48 -1
- data/elastic-transport.gemspec +6 -9
- data/lib/elastic/transport/client.rb +66 -45
- data/lib/elastic/transport/meta_header.rb +21 -12
- data/lib/elastic/transport/opentelemetry.rb +166 -0
- data/lib/elastic/transport/transport/base.rb +74 -54
- data/lib/elastic/transport/transport/errors.rb +2 -4
- data/lib/elastic/transport/transport/http/curb.rb +30 -26
- data/lib/elastic/transport/transport/http/faraday.rb +30 -27
- data/lib/elastic/transport/transport/http/manticore.rb +10 -4
- data/lib/elastic/transport/transport/response.rb +3 -3
- data/lib/elastic/transport/transport/serializer/multi_json.rb +3 -3
- data/lib/elastic/transport/transport/sniffer.rb +3 -1
- data/lib/elastic/transport/version.rb +1 -1
- data/lib/elastic/transport.rb +1 -0
- data/spec/elastic/transport/base_spec.rb +26 -25
- data/spec/elastic/transport/client_spec.rb +91 -18
- data/spec/elastic/transport/http/manticore_spec.rb +20 -2
- data/spec/elastic/transport/meta_header_spec.rb +26 -11
- data/spec/elastic/transport/opentelemetry_spec.rb +325 -0
- data/spec/elastic/transport/sniffer_spec.rb +18 -0
- data/spec/spec_helper.rb +16 -1
- data/test/integration/jruby_test.rb +1 -1
- data/test/integration/transport_test.rb +86 -40
- data/test/test_helper.rb +9 -6
- data/test/unit/adapters_test.rb +104 -0
- data/test/unit/connection_test.rb +35 -37
- data/test/unit/transport_base_test.rb +7 -8
- data/test/unit/transport_curb_test.rb +2 -3
- data/test/unit/transport_manticore_test.rb +1 -1
- metadata +23 -76
@@ -24,12 +24,12 @@ module Elastic
|
|
24
24
|
include Loggable
|
25
25
|
|
26
26
|
DEFAULT_PORT = 9200
|
27
|
-
DEFAULT_PROTOCOL = 'http'
|
27
|
+
DEFAULT_PROTOCOL = 'http'.freeze
|
28
28
|
DEFAULT_RELOAD_AFTER = 10_000 # Requests
|
29
29
|
DEFAULT_RESURRECT_AFTER = 60 # Seconds
|
30
30
|
DEFAULT_MAX_RETRIES = 3 # Requests
|
31
31
|
DEFAULT_SERIALIZER_CLASS = Serializer::MultiJson
|
32
|
-
SANITIZED_PASSWORD = '*' * (rand(14)+1)
|
32
|
+
SANITIZED_PASSWORD = '*' * (rand(14) + 1)
|
33
33
|
|
34
34
|
attr_reader :hosts, :options, :connections, :counter, :last_request_at, :protocol
|
35
35
|
attr_accessor :serializer, :sniffer, :logger, :tracer,
|
@@ -59,7 +59,7 @@ module Elastic
|
|
59
59
|
@compression = !!@options[:compression]
|
60
60
|
@connections = __build_connections
|
61
61
|
|
62
|
-
@serializer = options[:serializer] || ( options[:serializer_class] ? options[:serializer_class].new(self) : DEFAULT_SERIALIZER_CLASS.new(self)
|
62
|
+
@serializer = options[:serializer] || ( options[:serializer_class] ? options[:serializer_class].new(self) : DEFAULT_SERIALIZER_CLASS.new(self))
|
63
63
|
@protocol = options[:protocol] || DEFAULT_PROTOCOL
|
64
64
|
|
65
65
|
@logger = options[:logger]
|
@@ -72,7 +72,7 @@ module Elastic
|
|
72
72
|
@reload_connections = options[:reload_connections]
|
73
73
|
@reload_after = options[:reload_connections].is_a?(Integer) ? options[:reload_connections] : DEFAULT_RELOAD_AFTER
|
74
74
|
@resurrect_after = options[:resurrect_after] || DEFAULT_RESURRECT_AFTER
|
75
|
-
@retry_on_status = Array(options[:retry_on_status]).map
|
75
|
+
@retry_on_status = Array(options[:retry_on_status]).map(&:to_i)
|
76
76
|
end
|
77
77
|
|
78
78
|
# Returns a connection from the connection pool by delegating to {Connections::Collection#get_connection}.
|
@@ -83,11 +83,11 @@ module Elastic
|
|
83
83
|
# @return [Connections::Connection]
|
84
84
|
# @see Connections::Collection#get_connection
|
85
85
|
#
|
86
|
-
def get_connection(options={})
|
86
|
+
def get_connection(options = {})
|
87
87
|
resurrect_dead_connections! if Time.now > @last_request_at + @resurrect_after
|
88
88
|
|
89
89
|
@counter_mtx.synchronize { @counter += 1 }
|
90
|
-
reload_connections!
|
90
|
+
reload_connections! if reload_connections && (counter % reload_after).zero?
|
91
91
|
connections.get_connection(options)
|
92
92
|
end
|
93
93
|
|
@@ -97,10 +97,10 @@ module Elastic
|
|
97
97
|
#
|
98
98
|
def reload_connections!
|
99
99
|
hosts = sniffer.hosts
|
100
|
-
__rebuild_connections
|
100
|
+
__rebuild_connections(hosts: hosts, options: options)
|
101
101
|
self
|
102
102
|
rescue SnifferTimeoutError
|
103
|
-
log_error
|
103
|
+
log_error('[SnifferTimeoutError] Timeout when reloading connections.')
|
104
104
|
self
|
105
105
|
end
|
106
106
|
|
@@ -109,7 +109,7 @@ module Elastic
|
|
109
109
|
# @see Connections::Connection#resurrect!
|
110
110
|
#
|
111
111
|
def resurrect_dead_connections!
|
112
|
-
connections.dead.each
|
112
|
+
connections.dead.each(&:resurrect!)
|
113
113
|
end
|
114
114
|
|
115
115
|
# Rebuilds the connections collection in the transport.
|
@@ -120,7 +120,7 @@ module Elastic
|
|
120
120
|
# @return [Connections::Collection]
|
121
121
|
# @api private
|
122
122
|
#
|
123
|
-
def __rebuild_connections(arguments={})
|
123
|
+
def __rebuild_connections(arguments = {})
|
124
124
|
@state_mutex.synchronize do
|
125
125
|
@hosts = arguments[:hosts] || []
|
126
126
|
@options = arguments[:options] || {}
|
@@ -128,7 +128,7 @@ module Elastic
|
|
128
128
|
__close_connections
|
129
129
|
|
130
130
|
new_connections = __build_connections
|
131
|
-
stale_connections = @connections.all.
|
131
|
+
stale_connections = @connections.all.reject { |c| new_connections.include?(c) }
|
132
132
|
new_connections = new_connections.reject { |c| @connections.all.include?(c) }
|
133
133
|
|
134
134
|
@connections.remove(stale_connections)
|
@@ -177,8 +177,8 @@ module Elastic
|
|
177
177
|
# @return [Connections::Connection]
|
178
178
|
# @api private
|
179
179
|
#
|
180
|
-
def __build_connection(host, options={}, block=nil)
|
181
|
-
raise NoMethodError,
|
180
|
+
def __build_connection(host, options = {}, block = nil)
|
181
|
+
raise NoMethodError, 'Implement this method in your class'
|
182
182
|
end
|
183
183
|
|
184
184
|
# Closes the connections collection
|
@@ -209,14 +209,14 @@ module Elastic
|
|
209
209
|
#
|
210
210
|
def __trace(method, path, params, headers, body, url, response, json, took, duration)
|
211
211
|
trace_url = "http://localhost:9200/#{path}?pretty" +
|
212
|
-
(
|
212
|
+
(params.empty? ? '' : "&#{::Faraday::Utils::ParamsHash[params].to_query}")
|
213
213
|
trace_body = body ? " -d '#{__convert_to_json(body, :pretty => true)}'" : ''
|
214
214
|
trace_command = "curl -X #{method.to_s.upcase}"
|
215
|
-
trace_command += " -H '#{headers.collect { |k,v| "#{k}: #{v}" }.join(", ")}'" if headers && !headers.empty?
|
215
|
+
trace_command += " -H '#{headers.collect { |k, v| "#{k}: #{v}" }.join(", ")}'" if headers && !headers.empty?
|
216
216
|
trace_command += " '#{trace_url}'#{trace_body}\n"
|
217
217
|
tracer.info trace_command
|
218
218
|
tracer.debug "# #{Time.now.iso8601} [#{response.status}] (#{format('%.3f', duration)}s)\n#"
|
219
|
-
tracer.debug json ? serializer.dump(json, :
|
219
|
+
tracer.debug json ? serializer.dump(json, pretty: true).gsub(/^/, '# ').sub(/\}$/, "\n# }")+"\n" : "# #{response.body}\n"
|
220
220
|
end
|
221
221
|
|
222
222
|
# Raise error specific for the HTTP response status or a generic server error
|
@@ -232,7 +232,7 @@ module Elastic
|
|
232
232
|
#
|
233
233
|
# @api private
|
234
234
|
#
|
235
|
-
def __convert_to_json(o=nil, options={})
|
235
|
+
def __convert_to_json(o = nil, options = {})
|
236
236
|
o.is_a?(String) ? o : serializer.dump(o, options)
|
237
237
|
end
|
238
238
|
|
@@ -276,40 +276,37 @@ module Elastic
|
|
276
276
|
reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
|
277
277
|
delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
|
278
278
|
|
279
|
-
max_retries =
|
280
|
-
opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
|
281
|
-
elsif options.key?(:retry_on_failure)
|
282
|
-
options[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : options[:retry_on_failure]
|
283
|
-
end
|
279
|
+
max_retries = max_retries(opts) || max_retries(options)
|
284
280
|
|
285
281
|
params = params.clone
|
286
|
-
|
282
|
+
# Transforms ignore status codes to Integer
|
283
|
+
ignore = Array(params.delete(:ignore)).compact.map(&:to_i)
|
287
284
|
|
288
285
|
begin
|
289
286
|
sleep(delay_on_retry / 1000.0) if tries > 0
|
290
|
-
tries
|
287
|
+
tries += 1
|
291
288
|
connection = get_connection or raise Error.new('Cannot get new connection from pool.')
|
292
289
|
|
293
|
-
if connection.connection.respond_to?(:params) &&
|
290
|
+
if connection.connection.respond_to?(:params) &&
|
291
|
+
connection.connection.params.respond_to?(:to_hash)
|
294
292
|
params = connection.connection.params.merge(params.to_hash)
|
295
293
|
end
|
296
294
|
|
297
|
-
url
|
295
|
+
url = connection.full_url(path, params)
|
298
296
|
response = block.call(connection, url)
|
299
|
-
connection.healthy! if connection.failures
|
297
|
+
connection.healthy! if connection.failures.positive?
|
300
298
|
|
301
299
|
# Raise an exception so we can catch it for `retry_on_status`
|
302
|
-
__raise_transport_error(response) if response.status.to_i >= 300 &&
|
300
|
+
__raise_transport_error(response) if response.status.to_i >= 300 &&
|
301
|
+
@retry_on_status.include?(response.status.to_i)
|
303
302
|
rescue Elastic::Transport::Transport::ServerError => e
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
log_fatal "[#{e.class}] Cannot get response from #{url} after #{tries} tries"
|
310
|
-
raise e
|
311
|
-
end
|
303
|
+
raise e unless response && @retry_on_status.include?(response.status)
|
304
|
+
|
305
|
+
log_warn "[#{e.class}] Attempt #{tries} to get response from #{url}"
|
306
|
+
if tries <= (max_retries || DEFAULT_MAX_RETRIES)
|
307
|
+
retry
|
312
308
|
else
|
309
|
+
log_fatal "[#{e.class}] Cannot get response from #{url} after #{tries} tries"
|
313
310
|
raise e
|
314
311
|
end
|
315
312
|
rescue *host_unreachable_exceptions => e
|
@@ -317,21 +314,21 @@ module Elastic
|
|
317
314
|
|
318
315
|
connection.dead!
|
319
316
|
|
320
|
-
if reload_on_failure
|
317
|
+
if reload_on_failure && tries < connections.all.size
|
321
318
|
log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
|
322
319
|
reload_connections! and retry
|
323
320
|
end
|
324
321
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
end
|
322
|
+
exception = Elastic::Transport::Transport::Error.new(e.message)
|
323
|
+
|
324
|
+
raise exception unless max_retries
|
325
|
+
|
326
|
+
log_warn "[#{e.class}] Attempt #{tries} connecting to #{connection.host.inspect}"
|
327
|
+
if tries <= max_retries
|
328
|
+
retry
|
333
329
|
else
|
334
|
-
|
330
|
+
log_fatal "[#{e.class}] Cannot connect to #{connection.host.inspect} after #{tries} tries"
|
331
|
+
raise exception
|
335
332
|
end
|
336
333
|
rescue Exception => e
|
337
334
|
log_fatal "[#{e.class}] #{e.message} (#{connection.host.inspect if connection})"
|
@@ -349,8 +346,18 @@ module Elastic
|
|
349
346
|
__raise_transport_error response unless ignore.include?(response.status.to_i)
|
350
347
|
end
|
351
348
|
|
352
|
-
|
353
|
-
|
349
|
+
if response.body &&
|
350
|
+
!response.body.empty? &&
|
351
|
+
response.headers &&
|
352
|
+
response.headers["content-type"] =~ /json/
|
353
|
+
|
354
|
+
# Prevent Float value from automatically becoming BigDecimal when using Oj
|
355
|
+
load_options = {}
|
356
|
+
load_options[:mode] = :compat if ::MultiJson.adapter.to_s == "MultiJson::Adapters::Oj"
|
357
|
+
|
358
|
+
json = serializer.load(response.body, load_options)
|
359
|
+
end
|
360
|
+
took = (json['took'] ? sprintf('%.3fs', json['took'] / 1000.0) : 'n/a') rescue 'n/a'
|
354
361
|
__log_response(method, path, params, body, url, response, json, took, duration) unless ignore.include?(response.status.to_i)
|
355
362
|
__trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
|
356
363
|
log_warn(response.headers['warning']) if response.headers&.[]('warning')
|
@@ -372,17 +379,21 @@ module Elastic
|
|
372
379
|
private
|
373
380
|
|
374
381
|
USER_AGENT_STR = 'User-Agent'.freeze
|
375
|
-
USER_AGENT_REGEX = /user
|
382
|
+
USER_AGENT_REGEX = /user-?_?agent/
|
376
383
|
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
377
384
|
CONTENT_ENCODING = 'Content-Encoding'.freeze
|
378
385
|
CONTENT_TYPE_STR = 'Content-Type'.freeze
|
379
|
-
CONTENT_TYPE_REGEX = /content
|
386
|
+
CONTENT_TYPE_REGEX = /content-?_?type/
|
380
387
|
DEFAULT_CONTENT_TYPE = 'application/json'.freeze
|
381
388
|
GZIP = 'gzip'.freeze
|
382
389
|
GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
|
383
390
|
HEX_STRING_DIRECTIVE = 'H*'.freeze
|
384
391
|
RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
|
385
392
|
|
393
|
+
def max_retries(opts)
|
394
|
+
opts[:retry_on_failure] == true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
|
395
|
+
end
|
396
|
+
|
386
397
|
def compress_request(body, headers)
|
387
398
|
if body
|
388
399
|
headers ||= {}
|
@@ -409,7 +420,7 @@ module Elastic
|
|
409
420
|
|
410
421
|
io = StringIO.new(body)
|
411
422
|
gzip_reader = if RUBY_ENCODING
|
412
|
-
Zlib::GzipReader.new(io, :
|
423
|
+
Zlib::GzipReader.new(io, encoding: 'ASCII-8BIT')
|
413
424
|
else
|
414
425
|
Zlib::GzipReader.new(io)
|
415
426
|
end
|
@@ -427,7 +438,7 @@ module Elastic
|
|
427
438
|
end
|
428
439
|
|
429
440
|
def apply_headers(client, options)
|
430
|
-
headers = options[:headers] || {}
|
441
|
+
headers = options[:headers].clone || {}
|
431
442
|
headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
|
432
443
|
headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header(client)
|
433
444
|
client.headers[ACCEPT_ENCODING] = GZIP if use_compression?
|
@@ -442,7 +453,7 @@ module Elastic
|
|
442
453
|
end
|
443
454
|
end
|
444
455
|
|
445
|
-
def user_agent_header(
|
456
|
+
def user_agent_header(_client)
|
446
457
|
@user_agent ||= begin
|
447
458
|
meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
|
448
459
|
if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
|
@@ -453,12 +464,21 @@ module Elastic
|
|
453
464
|
end
|
454
465
|
|
455
466
|
def connection_headers(connection)
|
456
|
-
if defined?(Elastic::Transport::Transport::HTTP::Manticore) &&
|
467
|
+
if defined?(Elastic::Transport::Transport::HTTP::Manticore) &&
|
468
|
+
instance_of?(Elastic::Transport::Transport::HTTP::Manticore)
|
457
469
|
@request_options[:headers]
|
458
470
|
else
|
459
471
|
connection.connection.headers
|
460
472
|
end
|
461
473
|
end
|
474
|
+
|
475
|
+
def capture_otel_span_attributes(connection, url)
|
476
|
+
if defined?(::OpenTelemetry)
|
477
|
+
::OpenTelemetry::Trace.current_span&.set_attribute('url.full', url)
|
478
|
+
::OpenTelemetry::Trace.current_span&.set_attribute('server.address', connection.host[:host])
|
479
|
+
::OpenTelemetry::Trace.current_span&.set_attribute('server.port', connection.host[:port].to_i)
|
480
|
+
end
|
481
|
+
end
|
462
482
|
end
|
463
483
|
end
|
464
484
|
end
|
@@ -18,7 +18,6 @@
|
|
18
18
|
module Elastic
|
19
19
|
module Transport
|
20
20
|
module Transport
|
21
|
-
|
22
21
|
# Generic client error
|
23
22
|
#
|
24
23
|
class Error < StandardError; end
|
@@ -78,14 +77,13 @@ module Elastic
|
|
78
77
|
505 => 'HTTPVersionNotSupported',
|
79
78
|
506 => 'VariantAlsoNegotiates',
|
80
79
|
510 => 'NotExtended'
|
81
|
-
}
|
80
|
+
}.freeze
|
82
81
|
|
83
|
-
ERRORS = HTTP_STATUSES.
|
82
|
+
ERRORS = HTTP_STATUSES.each_with_object({}) do |error, sum|
|
84
83
|
status, name = error
|
85
84
|
sum[status] = Errors.const_set name, Class.new(ServerError)
|
86
85
|
sum
|
87
86
|
end
|
88
|
-
|
89
87
|
end
|
90
88
|
end
|
91
89
|
end
|
@@ -31,37 +31,41 @@ module Elastic
|
|
31
31
|
# @see Transport::Base#perform_request
|
32
32
|
#
|
33
33
|
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
34
|
-
super do |connection,
|
34
|
+
super do |connection, url|
|
35
|
+
capture_otel_span_attributes(connection, url)
|
35
36
|
connection.connection.url = connection.full_url(path, params)
|
36
37
|
body = body ? __convert_to_json(body) : nil
|
37
38
|
body, headers = compress_request(body, headers)
|
38
39
|
|
39
40
|
case method
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
when 'HEAD'
|
42
|
+
connection.connection.set :nobody, true
|
43
|
+
when 'GET', 'POST', 'PUT', 'DELETE'
|
44
|
+
connection.connection.set :nobody, false
|
45
|
+
connection.connection.put_data = body if body
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
47
|
+
if headers
|
48
|
+
if connection.connection.headers
|
49
|
+
connection.connection.headers.merge!(headers)
|
50
|
+
else
|
51
|
+
connection.connection.headers = headers
|
52
52
|
end
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
+
else raise ArgumentError, "Unsupported HTTP method: #{method}"
|
55
56
|
end
|
56
57
|
|
57
58
|
connection.connection.http(method.to_sym)
|
59
|
+
header_string = connection.connection.header_str.to_s
|
58
60
|
|
59
|
-
response_headers =
|
60
|
-
response_headers
|
61
|
+
_response_status, *response_headers = header_string.split(/[\r\n]+/).map(&:strip)
|
62
|
+
response_headers = Hash[response_headers.flat_map { |s| s.scan(/^(\S+): (.+)/) }].transform_keys(&:downcase)
|
61
63
|
|
62
|
-
Response.new
|
63
|
-
|
64
|
-
|
64
|
+
Response.new(
|
65
|
+
connection.connection.response_code,
|
66
|
+
decompress_response(connection.connection.body_str),
|
67
|
+
response_headers
|
68
|
+
)
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
@@ -73,7 +77,7 @@ module Elastic
|
|
73
77
|
client = ::Curl::Easy.new
|
74
78
|
|
75
79
|
apply_headers(client, options)
|
76
|
-
client.url
|
80
|
+
client.url = __full_url(host)
|
77
81
|
|
78
82
|
if host[:user]
|
79
83
|
client.http_auth_types = host[:auth_type] || :basic
|
@@ -105,13 +109,13 @@ module Elastic
|
|
105
109
|
|
106
110
|
def user_agent_header(client)
|
107
111
|
@user_agent ||= begin
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
|
113
|
+
if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
|
114
|
+
meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
|
115
|
+
end
|
116
|
+
meta << "Curb #{Curl::CURB_VERSION}"
|
117
|
+
"elastic-transport-ruby/#{VERSION} (#{meta.join('; ')})"
|
118
|
+
end
|
115
119
|
end
|
116
120
|
end
|
117
121
|
end
|
@@ -34,26 +34,28 @@ module Elastic
|
|
34
34
|
#
|
35
35
|
def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
|
36
36
|
super do |connection, url|
|
37
|
-
|
38
|
-
|
39
|
-
connection.connection.headers.merge(headers)
|
40
|
-
else
|
41
|
-
connection.connection.headers
|
42
|
-
end
|
43
|
-
else
|
44
|
-
headers
|
45
|
-
end
|
37
|
+
capture_otel_span_attributes(connection, url)
|
38
|
+
headers = parse_headers(headers, connection)
|
46
39
|
body = body ? __convert_to_json(body) : nil
|
47
40
|
body, headers = compress_request(body, headers)
|
48
41
|
|
49
|
-
response = connection.connection.run_request(
|
50
|
-
method.downcase.to_sym,
|
51
|
-
url,
|
52
|
-
body,
|
53
|
-
headers
|
54
|
-
)
|
42
|
+
response = connection.connection.run_request(method.downcase.to_sym, url, body, headers)
|
55
43
|
|
56
|
-
Response.new
|
44
|
+
Response.new(response.status, decompress_response(response.body), response.headers)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Merges headers already present in the connection and the ones passed in to perform_request
|
49
|
+
#
|
50
|
+
def parse_headers(headers, connection)
|
51
|
+
if connection.connection.headers
|
52
|
+
if !headers.nil?
|
53
|
+
connection.connection.headers.merge(headers)
|
54
|
+
else
|
55
|
+
connection.connection.headers
|
56
|
+
end
|
57
|
+
else
|
58
|
+
headers
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
@@ -73,10 +75,10 @@ module Elastic
|
|
73
75
|
#
|
74
76
|
def host_unreachable_exceptions
|
75
77
|
[
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
::Faraday::ConnectionFailed,
|
79
|
+
::Faraday::TimeoutError,
|
80
|
+
::Faraday.const_defined?(:ServerError) ? ::Faraday::ServerError : nil,
|
81
|
+
::Faraday::SSLError
|
80
82
|
].compact
|
81
83
|
end
|
82
84
|
|
@@ -84,13 +86,14 @@ module Elastic
|
|
84
86
|
|
85
87
|
def user_agent_header(client)
|
86
88
|
@user_agent ||= begin
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
89
|
+
meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
|
90
|
+
if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
|
91
|
+
meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} " \
|
92
|
+
"#{RbConfig::CONFIG['target_cpu']}"
|
93
|
+
end
|
94
|
+
meta << client.headers[USER_AGENT_STR]
|
95
|
+
"elastic-transport-ruby/#{VERSION} (#{meta.join('; ')})"
|
96
|
+
end
|
94
97
|
end
|
95
98
|
end
|
96
99
|
end
|
@@ -89,12 +89,12 @@ module Elastic
|
|
89
89
|
#
|
90
90
|
def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
|
91
91
|
super do |connection, url|
|
92
|
+
capture_otel_span_attributes(connection, url)
|
92
93
|
body = body ? __convert_to_json(body) : nil
|
93
|
-
body, headers = compress_request(body,
|
94
|
-
|
94
|
+
body, headers = compress_request(body, parse_headers(headers))
|
95
95
|
params[:body] = body if body
|
96
96
|
params[:headers] = headers if headers
|
97
|
-
|
97
|
+
|
98
98
|
case method
|
99
99
|
when 'GET'
|
100
100
|
resp = connection.connection.get(url, params)
|
@@ -161,8 +161,14 @@ module Elastic
|
|
161
161
|
|
162
162
|
private
|
163
163
|
|
164
|
+
def parse_headers(headers)
|
165
|
+
request_headers = @request_options.fetch(:headers, {})
|
166
|
+
headers = request_headers.merge(headers || {})
|
167
|
+
headers.empty? ? nil : headers
|
168
|
+
end
|
169
|
+
|
164
170
|
def apply_headers(options)
|
165
|
-
headers = options[:headers] || options.dig(:transport_options, :headers) || {}
|
171
|
+
headers = options[:headers].clone || options.dig(:transport_options, :headers).clone || {}
|
166
172
|
headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
|
167
173
|
headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || find_value(@request_options[:headers], USER_AGENT_REGEX) || user_agent_header
|
168
174
|
headers[ACCEPT_ENCODING] = GZIP if use_compression?
|
@@ -19,16 +19,16 @@ module Elastic
|
|
19
19
|
module Transport
|
20
20
|
module Transport
|
21
21
|
# Wraps the response from Elasticsearch.
|
22
|
-
#
|
22
|
+
# It provides `body`, `status` and `headers` methods
|
23
23
|
class Response
|
24
24
|
attr_reader :status, :body, :headers
|
25
25
|
|
26
26
|
# @param status [Integer] Response status code
|
27
27
|
# @param body [String] Response body
|
28
28
|
# @param headers [Hash] Response headers
|
29
|
-
def initialize(status, body, headers={})
|
29
|
+
def initialize(status, body, headers = {})
|
30
30
|
@status, @body, @headers = status, body, headers
|
31
|
-
@body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding)
|
31
|
+
@body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding) && !body.frozen?
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -24,7 +24,7 @@ module Elastic
|
|
24
24
|
module Base
|
25
25
|
# @param transport [Object] The instance of transport which uses this serializer
|
26
26
|
#
|
27
|
-
def initialize(transport=nil)
|
27
|
+
def initialize(transport = nil)
|
28
28
|
@transport = transport
|
29
29
|
end
|
30
30
|
end
|
@@ -36,13 +36,13 @@ module Elastic
|
|
36
36
|
|
37
37
|
# De-serialize a Hash from JSON string
|
38
38
|
#
|
39
|
-
def load(string, options={})
|
39
|
+
def load(string, options = {})
|
40
40
|
::MultiJson.load(string, options)
|
41
41
|
end
|
42
42
|
|
43
43
|
# Serialize a Hash to JSON string
|
44
44
|
#
|
45
|
-
def dump(object, options={})
|
45
|
+
def dump(object, options = {})
|
46
46
|
::MultiJson.dump(object, options)
|
47
47
|
end
|
48
48
|
end
|
@@ -76,7 +76,9 @@ module Elastic
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def parse_publish_address(publish_address)
|
79
|
-
# publish_address is in the format hostname/ip:port
|
79
|
+
# When publish_address is in the format 'inet[hostname/ip:port]'
|
80
|
+
return parse_address_port(publish_address[6..-2]) if publish_address =~ /^inet\[.*\]$/
|
81
|
+
|
80
82
|
if publish_address =~ /\//
|
81
83
|
parts = publish_address.partition('/')
|
82
84
|
[ parts[0], parse_address_port(parts[2])[1] ]
|
data/lib/elastic/transport.rb
CHANGED
@@ -34,5 +34,6 @@ require 'elastic/transport/transport/connections/collection'
|
|
34
34
|
require 'elastic/transport/transport/http/faraday'
|
35
35
|
require 'elastic/transport/client'
|
36
36
|
require 'elastic/transport/redacted'
|
37
|
+
require 'elastic/transport/opentelemetry'
|
37
38
|
|
38
39
|
require 'elastic/transport/version'
|