elasticsearch-transport 7.14.0 → 7.16.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/elasticsearch-transport.gemspec +4 -4
- data/lib/elasticsearch/transport/client.rb +31 -0
- data/lib/elasticsearch/transport/transport/base.rb +40 -20
- data/lib/elasticsearch/transport/transport/connections/connection.rb +2 -1
- data/lib/elasticsearch/transport/transport/http/curb.rb +6 -4
- data/lib/elasticsearch/transport/transport/http/faraday.rb +8 -7
- data/lib/elasticsearch/transport/transport/http/manticore.rb +7 -4
- data/lib/elasticsearch/transport/version.rb +1 -1
- data/lib/elasticsearch/transport.rb +1 -0
- data/spec/elasticsearch/transport/client_spec.rb +107 -24
- data/spec/elasticsearch/transport/http/curb_spec.rb +126 -0
- data/spec/elasticsearch/transport/http/faraday_spec.rb +141 -0
- data/spec/elasticsearch/transport/http/manticore_spec.rb +143 -0
- data/test/integration/jruby_test.rb +43 -0
- data/test/integration/transport_test.rb +18 -27
- data/test/unit/transport_manticore_test.rb +224 -155
- metadata +15 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86cb8f5de5ca5fc4270376d791ea8bd03252e5bbed8494d232da2eea8512434a
|
4
|
+
data.tar.gz: 27a6b7bfeea33bfa6e56ddd4a01109a1864a48685b54558707fe9af15ae73948
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2c129c4eeeb76a83d3fea12764ec135ad768920cd20c7c447a768708ab324db32482bdf616197e0674fe72a62ec74422b35774117a2d22cd0de8cc1520df20d
|
7
|
+
data.tar.gz: 586f14ab09c77a7a257667131d692dfd056e06ea66ccd3f53194985113043dec32e0fab5cf7fb3ed3d1a84036df02127df051c589ca137c6412ea8456a238a68
|
@@ -26,12 +26,12 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.authors = ['Karel Minarik']
|
27
27
|
s.email = ['karel.minarik@elasticsearch.org']
|
28
28
|
s.summary = 'Ruby client for Elasticsearch.'
|
29
|
-
s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.
|
29
|
+
s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.16/index.html'
|
30
30
|
s.license = 'Apache-2.0'
|
31
31
|
s.metadata = {
|
32
|
-
'homepage_uri' => 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.
|
33
|
-
'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/7.
|
34
|
-
'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/7.
|
32
|
+
'homepage_uri' => 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.16/index.html',
|
33
|
+
'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/7.16/CHANGELOG.md',
|
34
|
+
'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/7.16/elasticsearch-transport',
|
35
35
|
'bug_tracker_uri' => 'https://github.com/elastic/elasticsearch-ruby/issues'
|
36
36
|
}
|
37
37
|
s.files = `git ls-files`.split($/)
|
@@ -92,6 +92,8 @@ module Elasticsearch
|
|
92
92
|
#
|
93
93
|
# @option arguments [Boolean,Number] :retry_on_failure Retry X times when request fails before raising and
|
94
94
|
# exception (false by default)
|
95
|
+
# @option arguments [Number] :delay_on_retry Delay in milliseconds between each retry (0 by default)
|
96
|
+
#
|
95
97
|
# @option arguments Array<Number> :retry_on_status Retry when specific status codes are returned
|
96
98
|
#
|
97
99
|
# @option arguments [Boolean] :reload_on_failure Reload connections after failure (false by default)
|
@@ -126,6 +128,7 @@ module Elasticsearch
|
|
126
128
|
# if you're using X-Opaque-Id
|
127
129
|
# @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
|
128
130
|
# (Default: true)
|
131
|
+
# @option ca_fingerprint [String] :ca_fingerprint provide this value to only trust certificates that are signed by a specific CA certificate
|
129
132
|
#
|
130
133
|
# @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
|
131
134
|
#
|
@@ -136,6 +139,7 @@ module Elasticsearch
|
|
136
139
|
@arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
|
137
140
|
@arguments[:reload_connections] ||= false
|
138
141
|
@arguments[:retry_on_failure] ||= false
|
142
|
+
@arguments[:delay_on_retry] ||= 0
|
139
143
|
@arguments[:reload_on_failure] ||= false
|
140
144
|
@arguments[:randomize_hosts] ||= false
|
141
145
|
@arguments[:transport_options] ||= {}
|
@@ -156,6 +160,7 @@ module Elasticsearch
|
|
156
160
|
|
157
161
|
@send_get_body_as = @arguments[:send_get_body_as] || 'GET'
|
158
162
|
@opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
|
163
|
+
@ca_fingerprint = @arguments.delete(:ca_fingerprint)
|
159
164
|
|
160
165
|
if @arguments[:request_timeout]
|
161
166
|
@arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
|
@@ -188,6 +193,7 @@ module Elasticsearch
|
|
188
193
|
opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
|
189
194
|
headers.merge!('X-Opaque-Id' => opaque_id)
|
190
195
|
end
|
196
|
+
validate_ca_fingerprints if @ca_fingerprint
|
191
197
|
transport.perform_request(method, path, params, body, headers)
|
192
198
|
end
|
193
199
|
|
@@ -211,6 +217,31 @@ module Elasticsearch
|
|
211
217
|
)
|
212
218
|
end
|
213
219
|
|
220
|
+
def validate_ca_fingerprints
|
221
|
+
transport.connections.connections.each do |connection|
|
222
|
+
unless connection.host[:scheme] == 'https'
|
223
|
+
raise Elasticsearch::Transport::Transport::Error, 'CA fingerprinting can\'t be configured over http'
|
224
|
+
end
|
225
|
+
|
226
|
+
next if connection.verified
|
227
|
+
|
228
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
229
|
+
socket = TCPSocket.new(connection.host[:host], connection.host[:port])
|
230
|
+
ssl = OpenSSL::SSL::SSLSocket.new(socket, ctx)
|
231
|
+
ssl.connect
|
232
|
+
cert_store = ssl.peer_cert_chain
|
233
|
+
matching_certs = cert_store.select do |cert|
|
234
|
+
OpenSSL::Digest::SHA256.hexdigest(cert.to_der).upcase == @ca_fingerprint.upcase.gsub(':', '')
|
235
|
+
end
|
236
|
+
if matching_certs.empty?
|
237
|
+
raise Elasticsearch::Transport::Transport::Error,
|
238
|
+
'Server certificate CA fingerprint does not match the value configured in ca_fingerprint'
|
239
|
+
end
|
240
|
+
|
241
|
+
connection.verified = true
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
214
245
|
def add_header(header)
|
215
246
|
headers = @arguments[:transport_options]&.[](:headers) || {}
|
216
247
|
headers.merge!(header)
|
@@ -54,6 +54,7 @@ module Elasticsearch
|
|
54
54
|
@options = arguments[:options] || {}
|
55
55
|
@options[:http] ||= {}
|
56
56
|
@options[:retry_on_status] ||= []
|
57
|
+
@options[:delay_on_retry] ||= 0
|
57
58
|
|
58
59
|
@block = block
|
59
60
|
@compression = !!@options[:compression]
|
@@ -223,7 +224,7 @@ module Elasticsearch
|
|
223
224
|
# @api private
|
224
225
|
#
|
225
226
|
def __convert_to_json(o=nil, options={})
|
226
|
-
o
|
227
|
+
o.is_a?(String) ? o : serializer.dump(o, options)
|
227
228
|
end
|
228
229
|
|
229
230
|
# Returns a full URL based on information from host
|
@@ -264,6 +265,7 @@ module Elasticsearch
|
|
264
265
|
start = Time.now
|
265
266
|
tries = 0
|
266
267
|
reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
|
268
|
+
delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
|
267
269
|
|
268
270
|
max_retries = if opts.key?(:retry_on_failure)
|
269
271
|
opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
|
@@ -272,10 +274,10 @@ module Elasticsearch
|
|
272
274
|
end
|
273
275
|
|
274
276
|
params = params.clone
|
275
|
-
|
276
277
|
ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
|
277
278
|
|
278
279
|
begin
|
280
|
+
sleep(delay_on_retry / 1000.0) if tries > 0
|
279
281
|
tries += 1
|
280
282
|
connection = get_connection or raise Error.new('Cannot get new connection from pool.')
|
281
283
|
|
@@ -284,9 +286,7 @@ module Elasticsearch
|
|
284
286
|
end
|
285
287
|
|
286
288
|
url = connection.full_url(path, params)
|
287
|
-
|
288
289
|
response = block.call(connection, url)
|
289
|
-
|
290
290
|
connection.healthy! if connection.failures > 0
|
291
291
|
|
292
292
|
# Raise an exception so we can catch it for `retry_on_status`
|
@@ -309,7 +309,6 @@ module Elasticsearch
|
|
309
309
|
log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
|
310
310
|
|
311
311
|
connection.dead!
|
312
|
-
|
313
312
|
if reload_on_failure and tries < connections.all.size
|
314
313
|
log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
|
315
314
|
reload_connections! and retry
|
@@ -336,14 +335,10 @@ module Elasticsearch
|
|
336
335
|
duration = Time.now - start
|
337
336
|
|
338
337
|
if response.status.to_i >= 300
|
339
|
-
__log_response
|
340
|
-
__trace
|
341
|
-
|
338
|
+
__log_response(method, path, params, body, url, response, nil, 'N/A', duration)
|
339
|
+
__trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
|
342
340
|
# Log the failure only when `ignore` doesn't match the response status
|
343
|
-
unless ignore.include?(response.status.to_i)
|
344
|
-
log_fatal "[#{response.status}] #{response.body}"
|
345
|
-
end
|
346
|
-
|
341
|
+
log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i)
|
347
342
|
__raise_transport_error response unless ignore.include?(response.status.to_i)
|
348
343
|
end
|
349
344
|
|
@@ -354,10 +349,8 @@ module Elasticsearch
|
|
354
349
|
__log_response method, path, params, body, url, response, json, took, duration
|
355
350
|
end
|
356
351
|
|
357
|
-
__trace
|
358
|
-
|
359
|
-
warnings(response.headers['warning']) if response.headers&.[]('warning')
|
360
|
-
|
352
|
+
__trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
|
353
|
+
log_warn(response.headers['warning']) if response.headers&.[]('warning')
|
361
354
|
Response.new response.status, json || response.body, response.headers
|
362
355
|
ensure
|
363
356
|
@last_request_at = Time.now
|
@@ -376,17 +369,38 @@ module Elasticsearch
|
|
376
369
|
|
377
370
|
USER_AGENT_STR = 'User-Agent'.freeze
|
378
371
|
USER_AGENT_REGEX = /user\-?\_?agent/
|
372
|
+
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
373
|
+
CONTENT_ENCODING = 'Content-Encoding'.freeze
|
379
374
|
CONTENT_TYPE_STR = 'Content-Type'.freeze
|
380
375
|
CONTENT_TYPE_REGEX = /content\-?\_?type/
|
381
376
|
DEFAULT_CONTENT_TYPE = 'application/json'.freeze
|
382
377
|
GZIP = 'gzip'.freeze
|
383
|
-
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
384
378
|
GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
|
385
379
|
HEX_STRING_DIRECTIVE = 'H*'.freeze
|
386
380
|
RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
|
387
381
|
|
382
|
+
def compress_request(body, headers)
|
383
|
+
if body
|
384
|
+
headers ||= {}
|
385
|
+
|
386
|
+
if gzipped?(body)
|
387
|
+
headers[CONTENT_ENCODING] = GZIP
|
388
|
+
elsif use_compression?
|
389
|
+
headers[CONTENT_ENCODING] = GZIP
|
390
|
+
gzip = Zlib::GzipWriter.new(StringIO.new)
|
391
|
+
gzip << body
|
392
|
+
body = gzip.close.string
|
393
|
+
else
|
394
|
+
headers.delete(CONTENT_ENCODING)
|
395
|
+
end
|
396
|
+
elsif headers
|
397
|
+
headers.delete(CONTENT_ENCODING)
|
398
|
+
end
|
399
|
+
|
400
|
+
[body, headers]
|
401
|
+
end
|
402
|
+
|
388
403
|
def decompress_response(body)
|
389
|
-
return body unless use_compression?
|
390
404
|
return body unless gzipped?(body)
|
391
405
|
|
392
406
|
io = StringIO.new(body)
|
@@ -399,6 +413,8 @@ module Elasticsearch
|
|
399
413
|
end
|
400
414
|
|
401
415
|
def gzipped?(body)
|
416
|
+
return unless body && !body.empty?
|
417
|
+
|
402
418
|
body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
|
403
419
|
end
|
404
420
|
|
@@ -432,8 +448,12 @@ module Elasticsearch
|
|
432
448
|
end
|
433
449
|
end
|
434
450
|
|
435
|
-
def
|
436
|
-
|
451
|
+
def connection_headers(connection)
|
452
|
+
if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore
|
453
|
+
@request_options[:headers]
|
454
|
+
else
|
455
|
+
connection.connection.headers
|
456
|
+
end
|
437
457
|
end
|
438
458
|
end
|
439
459
|
end
|
@@ -33,6 +33,7 @@ module Elasticsearch
|
|
33
33
|
DEFAULT_RESURRECT_TIMEOUT = 60
|
34
34
|
|
35
35
|
attr_reader :host, :connection, :options, :failures, :dead_since
|
36
|
+
attr_accessor :verified
|
36
37
|
|
37
38
|
# @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`)
|
38
39
|
# @option arguments [Object] :connection The transport-specific physical connection or "session"
|
@@ -42,6 +43,7 @@ module Elasticsearch
|
|
42
43
|
@host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
|
43
44
|
@connection = arguments[:connection]
|
44
45
|
@options = arguments[:options] || {}
|
46
|
+
@verified = false
|
45
47
|
@state_mutex = Mutex.new
|
46
48
|
|
47
49
|
@options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT
|
@@ -153,7 +155,6 @@ module Elasticsearch
|
|
153
155
|
"<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
|
154
156
|
end
|
155
157
|
end
|
156
|
-
|
157
158
|
end
|
158
159
|
end
|
159
160
|
end
|
@@ -19,29 +19,31 @@ module Elasticsearch
|
|
19
19
|
module Transport
|
20
20
|
module Transport
|
21
21
|
module HTTP
|
22
|
-
|
23
22
|
# Alternative HTTP transport implementation, using the [_Curb_](https://rubygems.org/gems/curb) client.
|
24
23
|
#
|
25
24
|
# @see Transport::Base
|
26
25
|
#
|
27
26
|
class Curb
|
28
27
|
include Base
|
29
|
-
|
30
28
|
# Performs the request by invoking {Transport::Base#perform_request} with a block.
|
31
29
|
#
|
32
30
|
# @return [Response]
|
33
31
|
# @see Transport::Base#perform_request
|
34
32
|
#
|
35
33
|
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
36
|
-
super do |connection,
|
34
|
+
super do |connection, _url|
|
37
35
|
connection.connection.url = connection.full_url(path, params)
|
36
|
+
body = body ? __convert_to_json(body) : nil
|
37
|
+
body, headers = compress_request(body, headers)
|
38
38
|
|
39
39
|
case method
|
40
40
|
when 'HEAD'
|
41
41
|
connection.connection.set :nobody, true
|
42
42
|
when 'GET', 'POST', 'PUT', 'DELETE'
|
43
43
|
connection.connection.set :nobody, false
|
44
|
-
|
44
|
+
|
45
|
+
connection.connection.put_data = body if body
|
46
|
+
|
45
47
|
if headers
|
46
48
|
if connection.connection.headers
|
47
49
|
connection.connection.headers.merge!(headers)
|
@@ -44,12 +44,13 @@ module Elasticsearch
|
|
44
44
|
headers
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
body = body ? __convert_to_json(body) : nil
|
48
|
+
body, headers = compress_request(body, headers)
|
49
|
+
|
50
|
+
response = connection.connection.run_request(method.downcase.to_sym,
|
51
|
+
url,
|
52
|
+
body,
|
53
|
+
headers)
|
53
54
|
|
54
55
|
Response.new response.status, decompress_response(response.body), response.headers
|
55
56
|
end
|
@@ -62,7 +63,7 @@ module Elasticsearch
|
|
62
63
|
def __build_connection(host, options={}, block=nil)
|
63
64
|
client = ::Faraday.new(__full_url(host), options, &block)
|
64
65
|
apply_headers(client, options)
|
65
|
-
Connections::Connection.new
|
66
|
+
Connections::Connection.new(host: host, connection: client)
|
66
67
|
end
|
67
68
|
|
68
69
|
# Returns an array of implementation specific connection errors.
|
@@ -63,6 +63,7 @@ module Elasticsearch
|
|
63
63
|
include Base
|
64
64
|
|
65
65
|
def initialize(arguments={}, &block)
|
66
|
+
@request_options = { headers: (arguments.dig(:transport_options, :headers) || {}) }
|
66
67
|
@manticore = build_client(arguments[:options] || {})
|
67
68
|
super(arguments, &block)
|
68
69
|
end
|
@@ -82,7 +83,10 @@ module Elasticsearch
|
|
82
83
|
#
|
83
84
|
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
84
85
|
super do |connection, url|
|
85
|
-
|
86
|
+
body = body ? __convert_to_json(body) : nil
|
87
|
+
body, headers = compress_request(body, @request_options[:headers])
|
88
|
+
|
89
|
+
params[:body] = body if body
|
86
90
|
params[:headers] = headers if headers
|
87
91
|
params = params.merge @request_options
|
88
92
|
case method
|
@@ -109,7 +113,6 @@ module Elasticsearch
|
|
109
113
|
# @return [Connections::Collection]
|
110
114
|
#
|
111
115
|
def __build_connections
|
112
|
-
@request_options = {}
|
113
116
|
apply_headers(@request_options, options[:transport_options])
|
114
117
|
apply_headers(@request_options, options)
|
115
118
|
|
@@ -155,11 +158,11 @@ module Elasticsearch
|
|
155
158
|
private
|
156
159
|
|
157
160
|
def apply_headers(request_options, options)
|
158
|
-
headers =
|
161
|
+
headers = options&.[](:headers) || {}
|
159
162
|
headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
|
160
163
|
headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header
|
161
164
|
headers[ACCEPT_ENCODING] = GZIP if use_compression?
|
162
|
-
request_options.merge!(headers
|
165
|
+
request_options[:headers].merge!(headers)
|
163
166
|
end
|
164
167
|
|
165
168
|
def user_agent_header
|
@@ -1466,28 +1466,19 @@ describe Elasticsearch::Transport::Client do
|
|
1466
1466
|
end
|
1467
1467
|
|
1468
1468
|
context 'when Elasticsearch response includes a warning header' do
|
1469
|
+
let(:logger) { double('logger', warn: '', warn?: '', info?: '', info: '', debug?: '', debug: '') }
|
1469
1470
|
let(:client) do
|
1470
|
-
Elasticsearch::Transport::Client.new(hosts: hosts)
|
1471
|
+
Elasticsearch::Transport::Client.new(hosts: hosts, logger: logger)
|
1471
1472
|
end
|
1472
1473
|
|
1473
1474
|
let(:warning) { 'Elasticsearch warning: "deprecation warning"' }
|
1474
1475
|
|
1475
1476
|
it 'prints a warning' do
|
1476
|
-
|
1477
|
-
{ 'warning' => warning }
|
1478
|
-
end
|
1479
|
-
|
1480
|
-
begin
|
1481
|
-
stderr = $stderr
|
1482
|
-
fake_stderr = StringIO.new
|
1483
|
-
$stderr = fake_stderr
|
1484
|
-
|
1485
|
-
client.perform_request('GET', '/')
|
1486
|
-
fake_stderr.rewind
|
1487
|
-
expect(fake_stderr.string).to eq("warning: #{warning}\n")
|
1488
|
-
ensure
|
1489
|
-
$stderr = stderr
|
1477
|
+
expect_any_instance_of(Faraday::Connection).to receive(:run_request) do
|
1478
|
+
Elasticsearch::Transport::Transport::Response.new(200, {}, { 'warning' => warning })
|
1490
1479
|
end
|
1480
|
+
client.perform_request('GET', '/')
|
1481
|
+
expect(logger).to have_received(:warn).with(warning)
|
1491
1482
|
end
|
1492
1483
|
end
|
1493
1484
|
|
@@ -1668,6 +1659,29 @@ describe Elasticsearch::Transport::Client do
|
|
1668
1659
|
end
|
1669
1660
|
end
|
1670
1661
|
|
1662
|
+
context 'when retry_on_failure is true and delay_on_retry is specified' do
|
1663
|
+
context 'when a node is unreachable' do
|
1664
|
+
let(:hosts) do
|
1665
|
+
[ELASTICSEARCH_HOSTS.first, "foobar1", "foobar2"]
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
let(:options) do
|
1669
|
+
{ retry_on_failure: true, delay_on_retry: 3000 }
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
let(:responses) do
|
1673
|
+
5.times.collect do
|
1674
|
+
client.perform_request('GET', '_nodes/_local')
|
1675
|
+
end
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
it 'retries on failure' do
|
1679
|
+
allow_any_instance_of(Object).to receive(:sleep).with(3000 / 1000)
|
1680
|
+
expect(responses.all? { true }).to be(true)
|
1681
|
+
end
|
1682
|
+
end
|
1683
|
+
end
|
1684
|
+
|
1671
1685
|
context 'when reload_on_failure is true' do
|
1672
1686
|
|
1673
1687
|
let(:hosts) do
|
@@ -1727,7 +1741,7 @@ describe Elasticsearch::Transport::Client do
|
|
1727
1741
|
end
|
1728
1742
|
|
1729
1743
|
it 'sets the Accept-Encoding header' do
|
1730
|
-
expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
|
1744
|
+
expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
|
1731
1745
|
end
|
1732
1746
|
|
1733
1747
|
it 'preserves the other headers' do
|
@@ -1746,7 +1760,7 @@ describe Elasticsearch::Transport::Client do
|
|
1746
1760
|
end
|
1747
1761
|
|
1748
1762
|
it 'sets the Accept-Encoding header' do
|
1749
|
-
expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
|
1763
|
+
expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
|
1750
1764
|
end
|
1751
1765
|
|
1752
1766
|
it 'preserves the other headers' do
|
@@ -1765,7 +1779,7 @@ describe Elasticsearch::Transport::Client do
|
|
1765
1779
|
end
|
1766
1780
|
|
1767
1781
|
it 'sets the Accept-Encoding header' do
|
1768
|
-
expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
|
1782
|
+
expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
|
1769
1783
|
end
|
1770
1784
|
|
1771
1785
|
it 'preserves the other headers' do
|
@@ -1784,7 +1798,7 @@ describe Elasticsearch::Transport::Client do
|
|
1784
1798
|
end
|
1785
1799
|
|
1786
1800
|
it 'sets the Accept-Encoding header' do
|
1787
|
-
expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
|
1801
|
+
expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
|
1788
1802
|
end
|
1789
1803
|
|
1790
1804
|
it 'preserves the other headers' do
|
@@ -1803,7 +1817,7 @@ describe Elasticsearch::Transport::Client do
|
|
1803
1817
|
end
|
1804
1818
|
|
1805
1819
|
it 'sets the Accept-Encoding header' do
|
1806
|
-
expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
|
1820
|
+
expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
|
1807
1821
|
end
|
1808
1822
|
|
1809
1823
|
it 'preserves the other headers' do
|
@@ -1827,7 +1841,7 @@ describe Elasticsearch::Transport::Client do
|
|
1827
1841
|
end
|
1828
1842
|
|
1829
1843
|
it 'sets the Accept-Encoding header' do
|
1830
|
-
expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
|
1844
|
+
expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
|
1831
1845
|
end
|
1832
1846
|
|
1833
1847
|
it 'preserves the other headers' do
|
@@ -1895,7 +1909,6 @@ describe Elasticsearch::Transport::Client do
|
|
1895
1909
|
end
|
1896
1910
|
|
1897
1911
|
context 'when request headers are specified' do
|
1898
|
-
|
1899
1912
|
let(:response) do
|
1900
1913
|
client.perform_request('GET', '/', {}, nil, { 'Content-Type' => 'application/yaml' })
|
1901
1914
|
end
|
@@ -1906,9 +1919,7 @@ describe Elasticsearch::Transport::Client do
|
|
1906
1919
|
end
|
1907
1920
|
|
1908
1921
|
describe 'selector' do
|
1909
|
-
|
1910
1922
|
context 'when the round-robin selector is used' do
|
1911
|
-
|
1912
1923
|
let(:nodes) do
|
1913
1924
|
3.times.collect do
|
1914
1925
|
client.perform_request('GET', '_nodes/_local').body['nodes'].to_a[0][1]['name']
|
@@ -1989,4 +2000,76 @@ describe Elasticsearch::Transport::Client do
|
|
1989
2000
|
end
|
1990
2001
|
end
|
1991
2002
|
end
|
2003
|
+
|
2004
|
+
context 'CA Fingerprinting' do
|
2005
|
+
context 'when setting a ca_fingerprint' do
|
2006
|
+
after do
|
2007
|
+
File.delete('./certificate.crt')
|
2008
|
+
File.delete('./certificate.key')
|
2009
|
+
end
|
2010
|
+
|
2011
|
+
let(:certificate) do
|
2012
|
+
system(
|
2013
|
+
'openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj "/C=BE/O=Test/CN=Test"' \
|
2014
|
+
' -keyout certificate.key -out certificate.crt',
|
2015
|
+
err: File::NULL
|
2016
|
+
)
|
2017
|
+
OpenSSL::X509::Certificate.new File.read('./certificate.crt')
|
2018
|
+
end
|
2019
|
+
|
2020
|
+
let(:client) do
|
2021
|
+
Elasticsearch::Transport::Client.new(
|
2022
|
+
host: 'https://elastic:changeme@localhost:9200',
|
2023
|
+
ca_fingerprint: OpenSSL::Digest::SHA256.hexdigest(certificate.to_der)
|
2024
|
+
)
|
2025
|
+
end
|
2026
|
+
|
2027
|
+
it 'validates CA fingerprints on perform request' do
|
2028
|
+
expect(client.transport.connections.connections.map(&:verified).uniq).to eq [false]
|
2029
|
+
allow(client.transport).to receive(:perform_request) { 'Hello' }
|
2030
|
+
|
2031
|
+
server = double('server').as_null_object
|
2032
|
+
allow(TCPSocket).to receive(:new) { server }
|
2033
|
+
socket = double('socket')
|
2034
|
+
allow(OpenSSL::SSL::SSLSocket).to receive(:new) { socket }
|
2035
|
+
allow(socket).to receive(:connect) { nil }
|
2036
|
+
allow(socket).to receive(:peer_cert_chain) { [certificate] }
|
2037
|
+
|
2038
|
+
response = client.perform_request('GET', '/')
|
2039
|
+
expect(client.transport.connections.connections.map(&:verified).uniq).to eq [true]
|
2040
|
+
expect(response).to eq 'Hello'
|
2041
|
+
end
|
2042
|
+
end
|
2043
|
+
|
2044
|
+
context 'when using an http host' do
|
2045
|
+
let(:client) do
|
2046
|
+
Elasticsearch::Transport::Client.new(
|
2047
|
+
host: 'http://elastic:changeme@localhost:9200',
|
2048
|
+
ca_fingerprint: 'test'
|
2049
|
+
)
|
2050
|
+
end
|
2051
|
+
|
2052
|
+
it 'raises an error' do
|
2053
|
+
expect do
|
2054
|
+
client.perform_request('GET', '/')
|
2055
|
+
end.to raise_exception(Elasticsearch::Transport::Transport::Error)
|
2056
|
+
end
|
2057
|
+
end
|
2058
|
+
|
2059
|
+
context 'when not setting a ca_fingerprint' do
|
2060
|
+
let(:client) do
|
2061
|
+
Elasticsearch::Transport::Client.new(
|
2062
|
+
host: 'http://elastic:changeme@localhost:9200'
|
2063
|
+
)
|
2064
|
+
end
|
2065
|
+
|
2066
|
+
it 'has unvalidated connections' do
|
2067
|
+
allow(client).to receive(:validate_ca_fingerprints) { nil }
|
2068
|
+
allow(client.transport).to receive(:perform_request) { nil }
|
2069
|
+
|
2070
|
+
client.perform_request('GET', '/')
|
2071
|
+
expect(client).to_not have_received(:validate_ca_fingerprints)
|
2072
|
+
end
|
2073
|
+
end
|
2074
|
+
end
|
1992
2075
|
end
|