elasticsearch-transport 7.15.0 → 7.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 013ab25456f1d6d612e5aae61b72c761998ec6483141399482b7457ed6cfe697
4
- data.tar.gz: d5d88475a41abb557a8ec9557a2306bb3098941bf2b031c62f24c0ace9a94859
3
+ metadata.gz: 4fc7f318edc5730a123909637ce8cf74f32c4ddb2c9571e6744b4ad864e5c5d8
4
+ data.tar.gz: 11549daa6e4586a272f01e64ae56c01a510a57687fc0a06ecec58b35906f21d3
5
5
  SHA512:
6
- metadata.gz: 0db31f9306cecfcbddf99a4ebeb953c48ab4f949a5607fb58307601a7f90f0e859ee2d9016b68eea9d0cd13e080d56219a24b166dc7b2d68b3492cd6e24e0cb7
7
- data.tar.gz: 06bfbb100a2205868ab071aea96c9827a8fbcdd55530b64eb639b8f27e0244b5d908ed93f33f914e14d3afd38bac84abba43dfb68c9990bb34760a32d9ca4af8
6
+ metadata.gz: f5ea6b69fcb79dc9502b057195ef71bd6f60aca21170cae677e92510f01091199391ac38ce081af54fb269fa1600aeee81b586565317eac3065ff308c177f703
7
+ data.tar.gz: 9274659714937b8505a1a2252da28658900458982b3abe338838dbe8328e5408481351edb10623378a3e6f48cf8ca48a55a14f9739cc7e6e5650627b4a03a587
@@ -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.x/index.html'
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.x/index.html',
33
- 'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/7.x/CHANGELOG.md',
34
- 'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/7.x/elasticsearch-transport',
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
 
@@ -202,15 +208,41 @@ module Elasticsearch
202
208
 
203
209
  def set_compatibility_header
204
210
  return unless ['1', 'true'].include?(ENV['ELASTIC_CLIENT_APIVERSIONING'])
211
+ return if instance_variable_get('@options').dig(:transport_options, :headers, 'Accept')
205
212
 
206
213
  add_header(
207
214
  {
208
- 'Accept' => 'application/vnd.elasticsearch+json;compatible-with=7',
215
+ 'Accept' => 'application/vnd.elasticsearch+json; compatible-with=7',
209
216
  'Content-Type' => 'application/vnd.elasticsearch+json; compatible-with=7'
210
217
  }
211
218
  )
212
219
  end
213
220
 
221
+ def validate_ca_fingerprints
222
+ transport.connections.connections.each do |connection|
223
+ unless connection.host[:scheme] == 'https'
224
+ raise Elasticsearch::Transport::Transport::Error, 'CA fingerprinting can\'t be configured over http'
225
+ end
226
+
227
+ next if connection.verified
228
+
229
+ ctx = OpenSSL::SSL::SSLContext.new
230
+ socket = TCPSocket.new(connection.host[:host], connection.host[:port])
231
+ ssl = OpenSSL::SSL::SSLSocket.new(socket, ctx)
232
+ ssl.connect
233
+ cert_store = ssl.peer_cert_chain
234
+ matching_certs = cert_store.select do |cert|
235
+ OpenSSL::Digest::SHA256.hexdigest(cert.to_der).upcase == @ca_fingerprint.upcase.gsub(':', '')
236
+ end
237
+ if matching_certs.empty?
238
+ raise Elasticsearch::Transport::Transport::Error,
239
+ 'Server certificate CA fingerprint does not match the value configured in ca_fingerprint'
240
+ end
241
+
242
+ connection.verified = true
243
+ end
244
+ end
245
+
214
246
  def add_header(header)
215
247
  headers = @arguments[:transport_options]&.[](:headers) || {}
216
248
  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 = o.is_a?(String) ? o : serializer.dump(o, options)
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]
@@ -275,6 +277,7 @@ module Elasticsearch
275
277
  ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
276
278
 
277
279
  begin
280
+ sleep(delay_on_retry / 1000.0) if tries > 0
278
281
  tries += 1
279
282
  connection = get_connection or raise Error.new('Cannot get new connection from pool.')
280
283
 
@@ -306,7 +309,6 @@ module Elasticsearch
306
309
  log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
307
310
 
308
311
  connection.dead!
309
-
310
312
  if reload_on_failure and tries < connections.all.size
311
313
  log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
312
314
  reload_connections! and retry
@@ -348,7 +350,7 @@ module Elasticsearch
348
350
  end
349
351
 
350
352
  __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
351
- warnings(response.headers['warning']) if response.headers&.[]('warning')
353
+ log_warn(response.headers['warning']) if response.headers&.[]('warning')
352
354
  Response.new response.status, json || response.body, response.headers
353
355
  ensure
354
356
  @last_request_at = Time.now
@@ -367,17 +369,38 @@ module Elasticsearch
367
369
 
368
370
  USER_AGENT_STR = 'User-Agent'.freeze
369
371
  USER_AGENT_REGEX = /user\-?\_?agent/
372
+ ACCEPT_ENCODING = 'Accept-Encoding'.freeze
373
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
370
374
  CONTENT_TYPE_STR = 'Content-Type'.freeze
371
375
  CONTENT_TYPE_REGEX = /content\-?\_?type/
372
376
  DEFAULT_CONTENT_TYPE = 'application/json'.freeze
373
377
  GZIP = 'gzip'.freeze
374
- ACCEPT_ENCODING = 'Accept-Encoding'.freeze
375
378
  GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
376
379
  HEX_STRING_DIRECTIVE = 'H*'.freeze
377
380
  RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
378
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
+
379
403
  def decompress_response(body)
380
- return body unless use_compression?
381
404
  return body unless gzipped?(body)
382
405
 
383
406
  io = StringIO.new(body)
@@ -390,6 +413,8 @@ module Elasticsearch
390
413
  end
391
414
 
392
415
  def gzipped?(body)
416
+ return unless body && !body.empty?
417
+
393
418
  body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
394
419
  end
395
420
 
@@ -423,10 +448,6 @@ module Elasticsearch
423
448
  end
424
449
  end
425
450
 
426
- def warnings(warning)
427
- warn("warning: #{warning}")
428
- end
429
-
430
451
  def connection_headers(connection)
431
452
  if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore
432
453
  @request_options[:headers]
@@ -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, url|
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
- connection.connection.put_data = __convert_to_json(body) if body
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
- response = connection.connection.run_request(
48
- method.downcase.to_sym,
49
- url,
50
- (body ? __convert_to_json(body) : nil),
51
- headers
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 :host => host, :connection => client
66
+ Connections::Connection.new(host: host, connection: client)
66
67
  end
67
68
 
68
69
  # Returns an array of implementation specific connection errors.
@@ -83,7 +83,10 @@ module Elasticsearch
83
83
  #
84
84
  def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
85
85
  super do |connection, url|
86
- params[:body] = __convert_to_json(body) if body
86
+ body = body ? __convert_to_json(body) : nil
87
+ body, headers = compress_request(body, @request_options[:headers])
88
+
89
+ params[:body] = body if body
87
90
  params[:headers] = headers if headers
88
91
  params = params.merge @request_options
89
92
  case method
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Elasticsearch
19
19
  module Transport
20
- VERSION = '7.15.0'.freeze
20
+ VERSION = '7.17.0'.freeze
21
21
  end
22
22
  end
@@ -18,6 +18,7 @@
18
18
  require 'uri'
19
19
  require 'time'
20
20
  require 'timeout'
21
+ require 'zlib'
21
22
  require 'multi_json'
22
23
  require 'faraday'
23
24
 
@@ -1432,10 +1432,7 @@ describe Elasticsearch::Transport::Client do
1432
1432
  headers = client.transport.connections.first.connection.headers
1433
1433
 
1434
1434
  expect(headers['Content-Type']).to eq('application/vnd.elasticsearch+json; compatible-with=7')
1435
- expect(headers['Accept']).to eq('application/vnd.elasticsearch+json;compatible-with=7')
1436
-
1437
- response = client.perform_request('GET', '/')
1438
- expect(response.headers['content-type']).to eq('application/json; charset=UTF-8')
1435
+ expect(headers['Accept']).to eq('application/vnd.elasticsearch+json; compatible-with=7')
1439
1436
 
1440
1437
  ENV.delete('ELASTIC_CLIENT_APIVERSIONING')
1441
1438
  end
@@ -1466,28 +1463,19 @@ describe Elasticsearch::Transport::Client do
1466
1463
  end
1467
1464
 
1468
1465
  context 'when Elasticsearch response includes a warning header' do
1466
+ let(:logger) { double('logger', warn: '', warn?: '', info?: '', info: '', debug?: '', debug: '') }
1469
1467
  let(:client) do
1470
- Elasticsearch::Transport::Client.new(hosts: hosts)
1468
+ Elasticsearch::Transport::Client.new(hosts: hosts, logger: logger)
1471
1469
  end
1472
1470
 
1473
1471
  let(:warning) { 'Elasticsearch warning: "deprecation warning"' }
1474
1472
 
1475
1473
  it 'prints a warning' do
1476
- allow_any_instance_of(Elasticsearch::Transport::Transport::Response).to receive(:headers) do
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
1474
+ expect_any_instance_of(Faraday::Connection).to receive(:run_request) do
1475
+ Elasticsearch::Transport::Transport::Response.new(200, {}, { 'warning' => warning })
1490
1476
  end
1477
+ client.perform_request('GET', '/')
1478
+ expect(logger).to have_received(:warn).with(warning)
1491
1479
  end
1492
1480
  end
1493
1481
 
@@ -1668,6 +1656,29 @@ describe Elasticsearch::Transport::Client do
1668
1656
  end
1669
1657
  end
1670
1658
 
1659
+ context 'when retry_on_failure is true and delay_on_retry is specified' do
1660
+ context 'when a node is unreachable' do
1661
+ let(:hosts) do
1662
+ [ELASTICSEARCH_HOSTS.first, "foobar1", "foobar2"]
1663
+ end
1664
+
1665
+ let(:options) do
1666
+ { retry_on_failure: true, delay_on_retry: 3000 }
1667
+ end
1668
+
1669
+ let(:responses) do
1670
+ 5.times.collect do
1671
+ client.perform_request('GET', '_nodes/_local')
1672
+ end
1673
+ end
1674
+
1675
+ it 'retries on failure' do
1676
+ allow_any_instance_of(Object).to receive(:sleep).with(3000 / 1000)
1677
+ expect(responses.all? { true }).to be(true)
1678
+ end
1679
+ end
1680
+ end
1681
+
1671
1682
  context 'when reload_on_failure is true' do
1672
1683
 
1673
1684
  let(:hosts) do
@@ -1727,7 +1738,7 @@ describe Elasticsearch::Transport::Client do
1727
1738
  end
1728
1739
 
1729
1740
  it 'sets the Accept-Encoding header' do
1730
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1741
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1731
1742
  end
1732
1743
 
1733
1744
  it 'preserves the other headers' do
@@ -1746,7 +1757,7 @@ describe Elasticsearch::Transport::Client do
1746
1757
  end
1747
1758
 
1748
1759
  it 'sets the Accept-Encoding header' do
1749
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1760
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1750
1761
  end
1751
1762
 
1752
1763
  it 'preserves the other headers' do
@@ -1765,7 +1776,7 @@ describe Elasticsearch::Transport::Client do
1765
1776
  end
1766
1777
 
1767
1778
  it 'sets the Accept-Encoding header' do
1768
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1779
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1769
1780
  end
1770
1781
 
1771
1782
  it 'preserves the other headers' do
@@ -1784,7 +1795,7 @@ describe Elasticsearch::Transport::Client do
1784
1795
  end
1785
1796
 
1786
1797
  it 'sets the Accept-Encoding header' do
1787
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1798
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1788
1799
  end
1789
1800
 
1790
1801
  it 'preserves the other headers' do
@@ -1803,7 +1814,7 @@ describe Elasticsearch::Transport::Client do
1803
1814
  end
1804
1815
 
1805
1816
  it 'sets the Accept-Encoding header' do
1806
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1817
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1807
1818
  end
1808
1819
 
1809
1820
  it 'preserves the other headers' do
@@ -1827,7 +1838,7 @@ describe Elasticsearch::Transport::Client do
1827
1838
  end
1828
1839
 
1829
1840
  it 'sets the Accept-Encoding header' do
1830
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1841
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1831
1842
  end
1832
1843
 
1833
1844
  it 'preserves the other headers' do
@@ -1895,7 +1906,6 @@ describe Elasticsearch::Transport::Client do
1895
1906
  end
1896
1907
 
1897
1908
  context 'when request headers are specified' do
1898
-
1899
1909
  let(:response) do
1900
1910
  client.perform_request('GET', '/', {}, nil, { 'Content-Type' => 'application/yaml' })
1901
1911
  end
@@ -1906,9 +1916,7 @@ describe Elasticsearch::Transport::Client do
1906
1916
  end
1907
1917
 
1908
1918
  describe 'selector' do
1909
-
1910
1919
  context 'when the round-robin selector is used' do
1911
-
1912
1920
  let(:nodes) do
1913
1921
  3.times.collect do
1914
1922
  client.perform_request('GET', '_nodes/_local').body['nodes'].to_a[0][1]['name']
@@ -1989,4 +1997,76 @@ describe Elasticsearch::Transport::Client do
1989
1997
  end
1990
1998
  end
1991
1999
  end
2000
+
2001
+ context 'CA Fingerprinting' do
2002
+ context 'when setting a ca_fingerprint' do
2003
+ after do
2004
+ File.delete('./certificate.crt')
2005
+ File.delete('./certificate.key')
2006
+ end
2007
+
2008
+ let(:certificate) do
2009
+ system(
2010
+ 'openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj "/C=BE/O=Test/CN=Test"' \
2011
+ ' -keyout certificate.key -out certificate.crt',
2012
+ err: File::NULL
2013
+ )
2014
+ OpenSSL::X509::Certificate.new File.read('./certificate.crt')
2015
+ end
2016
+
2017
+ let(:client) do
2018
+ Elasticsearch::Transport::Client.new(
2019
+ host: 'https://elastic:changeme@localhost:9200',
2020
+ ca_fingerprint: OpenSSL::Digest::SHA256.hexdigest(certificate.to_der)
2021
+ )
2022
+ end
2023
+
2024
+ it 'validates CA fingerprints on perform request' do
2025
+ expect(client.transport.connections.connections.map(&:verified).uniq).to eq [false]
2026
+ allow(client.transport).to receive(:perform_request) { 'Hello' }
2027
+
2028
+ server = double('server').as_null_object
2029
+ allow(TCPSocket).to receive(:new) { server }
2030
+ socket = double('socket')
2031
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new) { socket }
2032
+ allow(socket).to receive(:connect) { nil }
2033
+ allow(socket).to receive(:peer_cert_chain) { [certificate] }
2034
+
2035
+ response = client.perform_request('GET', '/')
2036
+ expect(client.transport.connections.connections.map(&:verified).uniq).to eq [true]
2037
+ expect(response).to eq 'Hello'
2038
+ end
2039
+ end
2040
+
2041
+ context 'when using an http host' do
2042
+ let(:client) do
2043
+ Elasticsearch::Transport::Client.new(
2044
+ host: 'http://elastic:changeme@localhost:9200',
2045
+ ca_fingerprint: 'test'
2046
+ )
2047
+ end
2048
+
2049
+ it 'raises an error' do
2050
+ expect do
2051
+ client.perform_request('GET', '/')
2052
+ end.to raise_exception(Elasticsearch::Transport::Transport::Error)
2053
+ end
2054
+ end
2055
+
2056
+ context 'when not setting a ca_fingerprint' do
2057
+ let(:client) do
2058
+ Elasticsearch::Transport::Client.new(
2059
+ host: 'http://elastic:changeme@localhost:9200'
2060
+ )
2061
+ end
2062
+
2063
+ it 'has unvalidated connections' do
2064
+ allow(client).to receive(:validate_ca_fingerprints) { nil }
2065
+ allow(client.transport).to receive(:perform_request) { nil }
2066
+
2067
+ client.perform_request('GET', '/')
2068
+ expect(client).to_not have_received(:validate_ca_fingerprints)
2069
+ end
2070
+ end
2071
+ end
1992
2072
  end
@@ -0,0 +1,126 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ unless defined?(JRUBY_VERSION)
19
+ require_relative '../../../spec_helper'
20
+
21
+ describe Elasticsearch::Transport::Transport::HTTP::Curb do
22
+ let(:client) do
23
+ Elasticsearch::Transport::Client.new(transport_class: described_class)
24
+ end
25
+
26
+ describe '#perform_request' do
27
+ subject(:perform_request) { client.perform_request(*args) }
28
+ let(:args) do
29
+ ['POST', '/', {}, body, headers]
30
+ end
31
+ let(:body) { '{"foo":"bar"}' }
32
+ let(:headers) { { 'Content-Type' => 'application/x-ndjson' } }
33
+
34
+ before do
35
+ allow_any_instance_of(Curl::Easy).to receive(:http).and_return(true)
36
+ end
37
+
38
+ it 'convert body to json' do
39
+ expect(client.transport).to receive(:__convert_to_json).with(body)
40
+ perform_request
41
+ end
42
+
43
+ it 'call compress_request' do
44
+ expect(client.transport).to receive(:compress_request).with(body, headers)
45
+ perform_request
46
+ end
47
+
48
+ it 'return response' do
49
+ expect(perform_request).to be_kind_of(Elasticsearch::Transport::Transport::Response)
50
+ end
51
+
52
+ it 'put body' do
53
+ expect(client.transport.connections.first.connection).to receive('put_data=').with(body)
54
+ perform_request
55
+ end
56
+
57
+ context 'when body nil' do
58
+ let(:body) { nil }
59
+
60
+ it 'convert body to json' do
61
+ expect(client.transport).not_to receive(:__convert_to_json)
62
+ perform_request
63
+ end
64
+
65
+ it 'call compress_request' do
66
+ expect(client.transport).to receive(:compress_request).with(body, headers)
67
+ perform_request
68
+ end
69
+
70
+ it 'put body' do
71
+ expect(client.transport.connections.first.connection).not_to receive('put_data=')
72
+ perform_request
73
+ end
74
+ end
75
+
76
+ context 'when body is hash' do
77
+ let(:body) { { foo: 'bar' } }
78
+ let(:body_string) { '{"foo":"bar"}' }
79
+
80
+ it 'convert body to json' do
81
+ expect(client.transport).to receive(:__convert_to_json).with(body)
82
+ perform_request
83
+ end
84
+
85
+ it 'call compress_request' do
86
+ expect(client.transport).to receive(:compress_request).with(body_string, headers)
87
+ perform_request
88
+ end
89
+
90
+ it 'put body' do
91
+ expect(client.transport.connections.first.connection).to receive('put_data=').with(body_string)
92
+ perform_request
93
+ end
94
+ end
95
+
96
+ context 'when compression enabled' do
97
+ let(:client) do
98
+ Elasticsearch::Transport::Client.new(transport_class: described_class, compression: true)
99
+ end
100
+ let(:body_string) { '{"foo":"bar"}' }
101
+ let(:compressed_body) do
102
+ gzip = Zlib::GzipWriter.new(StringIO.new)
103
+ gzip << body_string
104
+ gzip.close.string
105
+ end
106
+
107
+ before { allow(client.transport).to receive(:decompress_response).and_return('') }
108
+
109
+ it 'put compressed body' do
110
+ expect(client.transport.connections.first.connection).to receive('put_data=').with(compressed_body)
111
+ perform_request
112
+ end
113
+
114
+ it 'set Content-Encoding header' do
115
+ perform_request
116
+ expect(client.transport.connections.first.connection.headers).to include('Content-Encoding')
117
+ end
118
+
119
+ it 'set Content-Encoding to gzip' do
120
+ perform_request
121
+ expect(client.transport.connections.first.connection.headers['Content-Encoding']).to eql('gzip')
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,141 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require_relative '../../../spec_helper'
19
+
20
+ describe Elasticsearch::Transport::Transport::HTTP::Faraday do
21
+ let(:client) do
22
+ Elasticsearch::Transport::Client.new(transport_class: described_class)
23
+ end
24
+
25
+ describe '#perform_request' do
26
+ subject(:perform_request) { client.perform_request(*args) }
27
+ let(:args) do
28
+ ['POST', '/', {}, body, headers]
29
+ end
30
+ let(:body) { '{"foo":"bar"}' }
31
+ let(:headers) { { 'Content-Type' => 'application/x-ndjson' } }
32
+ let(:response) { instance_double('Faraday::Response', status: 200, body: '', headers: headers) }
33
+ let(:expected_headers) do
34
+ client.transport.connections.first.connection.headers.merge(headers)
35
+ end
36
+
37
+ before do
38
+ allow_any_instance_of(Faraday::Connection).to receive(:run_request).and_return(response)
39
+ end
40
+
41
+ it 'convert body to json' do
42
+ expect(client.transport).to receive(:__convert_to_json).with(body)
43
+ perform_request
44
+ end
45
+
46
+ it 'call compress_request' do
47
+ expect(client.transport).to receive(:compress_request).with(body, expected_headers)
48
+ perform_request
49
+ end
50
+
51
+ it 'return response' do
52
+ expect(perform_request).to be_kind_of(Elasticsearch::Transport::Transport::Response)
53
+ end
54
+
55
+ it 'run body with preper params' do
56
+ expect(
57
+ client.transport.connections.first.connection
58
+ ).to receive(:run_request).with(:post, 'http://localhost:9200/', body, expected_headers).and_return(response)
59
+ perform_request
60
+ end
61
+
62
+ context 'when body nil' do
63
+ let(:body) { nil }
64
+ let(:request_params) { [:post, 'http://localhost:9200/', body, expected_headers] }
65
+
66
+ it 'convert body to json' do
67
+ expect(client.transport).not_to receive(:__convert_to_json)
68
+ perform_request
69
+ end
70
+
71
+ it 'call compress_request' do
72
+ expect(client.transport).to receive(:compress_request).with(body, expected_headers)
73
+ perform_request
74
+ end
75
+
76
+ it 'run body with preper params' do
77
+ expect(
78
+ client.transport.connections.first.connection
79
+ ).to receive(:run_request).with(*request_params).and_return(response)
80
+ perform_request
81
+ end
82
+ end
83
+
84
+ context 'when body is hash' do
85
+ let(:body) { { foo: 'bar' } }
86
+ let(:body_string) { '{"foo":"bar"}' }
87
+ let(:request_params) { [:post, 'http://localhost:9200/', body_string, expected_headers] }
88
+
89
+ it 'convert body to json' do
90
+ expect(client.transport).to receive(:__convert_to_json).with(body)
91
+ perform_request
92
+ end
93
+
94
+ it 'call compress_request' do
95
+ expect(client.transport).to receive(:compress_request).with(body_string, expected_headers)
96
+ perform_request
97
+ end
98
+
99
+ it 'run body with preper params' do
100
+ expect(
101
+ client.transport.connections.first.connection
102
+ ).to receive(:run_request).with(*request_params).and_return(response)
103
+ perform_request
104
+ end
105
+ end
106
+
107
+ context 'when compression enabled' do
108
+ let(:client) do
109
+ Elasticsearch::Transport::Client.new(transport_class: described_class, compression: true)
110
+ end
111
+ let(:body_string) { '{"foo":"bar"}' }
112
+ let(:expected_headers) { super().merge({ "Content-Encoding" => "gzip", "Accept-Encoding" => "gzip"}) }
113
+ let(:request_params) { [:post, 'http://localhost:9200/', compressed_body, expected_headers] }
114
+ let(:compressed_body) do
115
+ gzip = Zlib::GzipWriter.new(StringIO.new)
116
+ gzip << body_string
117
+ gzip.close.string
118
+ end
119
+
120
+ it 'run body with preper params' do
121
+ expect(
122
+ client.transport.connections.first.connection
123
+ ).to receive(:run_request).with(*request_params).and_return(response)
124
+ perform_request
125
+ end
126
+
127
+ context 'when client makes second request with nil boby' do
128
+ before { perform_request }
129
+
130
+ it 'remove Content-Encoding header' do
131
+ expected_headers.delete("Content-Encoding")
132
+ expect(
133
+ client.transport.connections.first.connection
134
+ ).to receive(:run_request).with(:post, 'http://localhost:9200/', nil, expected_headers)
135
+ .and_return(response)
136
+ client.perform_request('POST', '/', {}, nil, headers)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,143 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ if defined?(JRUBY_VERSION)
19
+ require_relative '../../../spec_helper'
20
+
21
+ describe Elasticsearch::Transport::Transport::HTTP::Manticore do
22
+ let(:client) do
23
+ Elasticsearch::Transport::Client.new(transport_class: described_class)
24
+ end
25
+
26
+ describe '#perform_request' do
27
+ subject(:perform_request) { client.perform_request(*args) }
28
+ let(:args) do
29
+ ['POST', '/', {}, body, headers]
30
+ end
31
+ let(:body) { '{"foo":"bar"}' }
32
+ let(:headers) { { 'Content-Type' => 'application/json' } }
33
+ let(:response) { instance_double('Manticore::Response', code: 200, read_body: '', headers: headers) }
34
+ let(:expected_headers) do
35
+ client.transport.instance_variable_get('@request_options')[:headers].merge(headers)
36
+ end
37
+
38
+ before do
39
+ allow_any_instance_of(Manticore::Client).to receive(:post).and_return(response)
40
+ end
41
+
42
+ it 'convert body to json' do
43
+ expect(client.transport).to receive(:__convert_to_json).with(body)
44
+ perform_request
45
+ end
46
+
47
+ it 'call compress_request' do
48
+ expect(client.transport).to receive(:compress_request).with(body, expected_headers)
49
+ perform_request
50
+ end
51
+
52
+ it 'return response' do
53
+ expect(perform_request).to be_kind_of(Elasticsearch::Transport::Transport::Response)
54
+ end
55
+
56
+ it 'run body with preper params' do
57
+ expect(
58
+ client.transport.connections.first.connection
59
+ ).to receive(:post).with('http://localhost:9200/', { body: body, headers: expected_headers }).and_return(response)
60
+ perform_request
61
+ end
62
+
63
+ context 'when body nil' do
64
+ let(:body) { nil }
65
+ let(:request_params) { ['http://localhost:9200/', { body: body, headers: expected_headers }] }
66
+
67
+ it 'convert body to json' do
68
+ expect(client.transport).not_to receive(:__convert_to_json)
69
+ perform_request
70
+ end
71
+
72
+ it 'call compress_request' do
73
+ expect(client.transport).to receive(:compress_request).with(body, expected_headers)
74
+ perform_request
75
+ end
76
+
77
+ it 'run body with preper params' do
78
+ expect(
79
+ client.transport.connections.first.connection
80
+ ).to receive(:post).with('http://localhost:9200/', { headers: expected_headers }).and_return(response)
81
+ perform_request
82
+ end
83
+ end
84
+
85
+ context 'when body is hash' do
86
+ let(:body) { { foo: 'bar' } }
87
+ let(:body_string) { '{"foo":"bar"}' }
88
+ let(:request_params) { ['http://localhost:9200/', { body: body_string, headers: expected_headers }] }
89
+
90
+ it 'convert body to json' do
91
+ expect(client.transport).to receive(:__convert_to_json).with(body)
92
+ perform_request
93
+ end
94
+
95
+ it 'call compress_request' do
96
+ expect(client.transport).to receive(:compress_request).with(body_string, expected_headers)
97
+ perform_request
98
+ end
99
+
100
+ it 'run body with preper params' do
101
+ expect(
102
+ client.transport.connections.first.connection
103
+ ).to receive(:post).with(*request_params).and_return(response)
104
+ perform_request
105
+ end
106
+ end
107
+
108
+ context 'when compression enabled' do
109
+ let(:client) do
110
+ Elasticsearch::Transport::Client.new(transport_class: described_class, compression: true)
111
+ end
112
+ let(:body_string) { '{"foo":"bar"}' }
113
+ let(:expected_headers) { super().merge({ "Content-Encoding" => "gzip", "Accept-Encoding" => "gzip"}) }
114
+ let(:request_params) { ['http://localhost:9200/', { body: compressed_body, headers: expected_headers }] }
115
+ let(:compressed_body) do
116
+ gzip = Zlib::GzipWriter.new(StringIO.new)
117
+ gzip << body_string
118
+ gzip.close.string
119
+ end
120
+
121
+ it 'run body with preper params' do
122
+ expect(
123
+ client.transport.connections.first.connection
124
+ ).to receive(:post).with(*request_params).and_return(response)
125
+ perform_request
126
+ end
127
+
128
+ context 'when client makes second request with nil boby' do
129
+ before { perform_request }
130
+
131
+ it 'remove Content-Encoding header' do
132
+ expected_headers.delete("Content-Encoding")
133
+ expect(
134
+ client.transport.connections.first.connection
135
+ ).to receive(:post).with('http://localhost:9200/', { headers: expected_headers })
136
+ .and_return(response)
137
+ client.perform_request('POST', '/', {}, nil, headers)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticsearch-transport
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.15.0
4
+ version: 7.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karel Minarik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-22 00:00:00.000000000 Z
11
+ date: 2022-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -392,6 +392,9 @@ files:
392
392
  - spec/elasticsearch/connections/selector_spec.rb
393
393
  - spec/elasticsearch/transport/base_spec.rb
394
394
  - spec/elasticsearch/transport/client_spec.rb
395
+ - spec/elasticsearch/transport/http/curb_spec.rb
396
+ - spec/elasticsearch/transport/http/faraday_spec.rb
397
+ - spec/elasticsearch/transport/http/manticore_spec.rb
395
398
  - spec/elasticsearch/transport/meta_header_spec.rb
396
399
  - spec/elasticsearch/transport/sniffer_spec.rb
397
400
  - spec/spec_helper.rb
@@ -406,13 +409,13 @@ files:
406
409
  - test/unit/transport_curb_test.rb
407
410
  - test/unit/transport_faraday_test.rb
408
411
  - test/unit/transport_manticore_test.rb
409
- homepage: https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.x/index.html
412
+ homepage: https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.16/index.html
410
413
  licenses:
411
414
  - Apache-2.0
412
415
  metadata:
413
- homepage_uri: https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.x/index.html
414
- changelog_uri: https://github.com/elastic/elasticsearch-ruby/blob/7.x/CHANGELOG.md
415
- source_code_uri: https://github.com/elastic/elasticsearch-ruby/tree/7.x/elasticsearch-transport
416
+ homepage_uri: https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.16/index.html
417
+ changelog_uri: https://github.com/elastic/elasticsearch-ruby/blob/7.16/CHANGELOG.md
418
+ source_code_uri: https://github.com/elastic/elasticsearch-ruby/tree/7.16/elasticsearch-transport
416
419
  bug_tracker_uri: https://github.com/elastic/elasticsearch-ruby/issues
417
420
  post_install_message:
418
421
  rdoc_options:
@@ -430,7 +433,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
430
433
  - !ruby/object:Gem::Version
431
434
  version: '0'
432
435
  requirements: []
433
- rubygems_version: 3.2.22
436
+ rubygems_version: 3.1.6
434
437
  signing_key:
435
438
  specification_version: 4
436
439
  summary: Ruby client for Elasticsearch.
@@ -439,6 +442,9 @@ test_files:
439
442
  - spec/elasticsearch/connections/selector_spec.rb
440
443
  - spec/elasticsearch/transport/base_spec.rb
441
444
  - spec/elasticsearch/transport/client_spec.rb
445
+ - spec/elasticsearch/transport/http/curb_spec.rb
446
+ - spec/elasticsearch/transport/http/faraday_spec.rb
447
+ - spec/elasticsearch/transport/http/manticore_spec.rb
442
448
  - spec/elasticsearch/transport/meta_header_spec.rb
443
449
  - spec/elasticsearch/transport/sniffer_spec.rb
444
450
  - spec/spec_helper.rb