elastic-transport 8.0.0.pre1 → 8.0.0.pre2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5f7efa80056792b4d863e1011b652c677df5083db1d328a336e4951e5f66bbb
4
- data.tar.gz: ade02810dfcc55b6f48f769019851d51f30018c3dc9b7eb2f95d380e91d9a2aa
3
+ metadata.gz: e2c04c9ca950389cb71ceda4d1fe1c7e4266c844ed95c73fd3b7815ea28c6134
4
+ data.tar.gz: 07deccf3f9278235cb4989cfaada2be0e271d21615f72fac8eeaab00e81058c6
5
5
  SHA512:
6
- metadata.gz: 964cfda300084d06334b4876a0a8f9e50f1f2242e34abc9708b2c771c39ea86846e068d1d9b3fd730ea92ba405d1f2b01cc2856d0ce8c6af65e6b4beb55bd90c
7
- data.tar.gz: c1f4b6b9f53bd72d272a041fb2578ff893cf6ddf91bcd3ec52a8c2b5ecdd4929f640fab783bce65ad4458f8914628bf8839476b9dfa846bd9e024125cd21d6a7
6
+ metadata.gz: 3e27a172d2fa106208b210fd4ab1cdd1bc76f6ef49e4b7aefae6a6a525dd650478e21c1e173335733c703f0119ec5e3935fdd0f46eb477573ae6cbceccc2ff34
7
+ data.tar.gz: a4cf7f2a840c75a43a6ed74d15f04fcab1c780fa9a2250892af38ca206c3eaa021f8548ed5a53f1339bb344bf41bc2225a92ed077ef227fdecd33d882b43f32e
data/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
- ## 8.0.0
1
+ ## 8.0.0.pre2
2
+
3
+ - Fixes tracing for Manticore [commit](https://github.com/elastic/elastic-transport-ruby/commit/98c81d19de4fee394f9c1a5079a1892ec951e0f9).
4
+ - Implements CA Fingerprinting [pull request](https://github.com/elastic/elastic-transport-ruby/pull/13)
5
+ - Adds `delay_on_retry` to wait between each failed connection, thanks @DinoPullerUqido! [commit](https://github.com/elastic/elastic-transport-ruby/commit/c2f8311409ca63a293588cb7eea5a0c672dbd436)
6
+ - Fixes compression, thanks @johnnyshields! [commit](https://github.com/elastic/elastic-transport-ruby/commit/8b326d643f76f037075500e19bbe096b2c298099)
7
+
8
+ ## 8.0.0.pre1
2
9
 
3
10
  - Library renamed from [`elasticsearch-transport`](https://github.com/elastic/elasticsearch-ruby/tree/7.x/elasticsearch-transport) to `elastic-transport` and promoted to its own repository.
4
11
 
data/Rakefile CHANGED
@@ -55,7 +55,6 @@ namespace :test do
55
55
  end
56
56
  end
57
57
 
58
-
59
58
  namespace :docker do
60
59
  desc <<~DOC
61
60
  Start Elasticsearch in a Docker container. Credentials are 'elastic:changeme'.
@@ -86,6 +86,8 @@ module Elastic
86
86
  #
87
87
  # @option arguments [Boolean,Number] :retry_on_failure Retry X times when request fails before raising and
88
88
  # exception (false by default)
89
+ # @option arguments [Number] :delay_on_retry Delay in milliseconds between each retry (0 by default)
90
+ #
89
91
  # @option arguments Array<Number> :retry_on_status Retry when specific status codes are returned
90
92
  #
91
93
  # @option arguments [Boolean] :reload_on_failure Reload connections after failure (false by default)
@@ -115,6 +117,7 @@ module Elastic
115
117
  #
116
118
  # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
117
119
  # (Default: true)
120
+ # @option ca_fingerprint [String] :ca_fingerprint provide this value to only trust certificates that are signed by a specific CA certificate
118
121
  #
119
122
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
120
123
  #
@@ -125,6 +128,7 @@ module Elastic
125
128
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
126
129
  @arguments[:reload_connections] ||= false
127
130
  @arguments[:retry_on_failure] ||= false
131
+ @arguments[:delay_on_retry] ||= 0
128
132
  @arguments[:reload_on_failure] ||= false
129
133
  @arguments[:randomize_hosts] ||= false
130
134
  @arguments[:transport_options] ||= {}
@@ -140,6 +144,7 @@ module Elastic
140
144
  DEFAULT_HOST)
141
145
 
142
146
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
147
+ @ca_fingerprint = @arguments.delete(:ca_fingerprint)
143
148
 
144
149
  if @arguments[:request_timeout]
145
150
  @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
@@ -167,11 +172,45 @@ module Elastic
167
172
  #
168
173
  def perform_request(method, path, params = {}, body = nil, headers = nil)
169
174
  method = @send_get_body_as if 'GET' == method && body
175
+ validate_ca_fingerprints if @ca_fingerprint
170
176
  transport.perform_request(method, path, params, body, headers)
171
177
  end
172
178
 
173
179
  private
174
180
 
181
+ def validate_ca_fingerprints
182
+ transport.connections.connections.each do |connection|
183
+ unless connection.host[:scheme] == 'https'
184
+ raise Elastic::Transport::Transport::Error, 'CA fingerprinting can\'t be configured over http'
185
+ end
186
+
187
+ next if connection.verified
188
+
189
+ ctx = OpenSSL::SSL::SSLContext.new
190
+ socket = TCPSocket.new(connection.host[:host], connection.host[:port])
191
+ ssl = OpenSSL::SSL::SSLSocket.new(socket, ctx)
192
+ ssl.connect
193
+ cert_store = ssl.peer_cert_chain
194
+ matching_certs = cert_store.select do |cert|
195
+ OpenSSL::Digest::SHA256.hexdigest(cert.to_der).upcase == @ca_fingerprint.gsub(':', '').upcase
196
+ end
197
+ if matching_certs.empty?
198
+ raise Elastic::Transport::Transport::Error,
199
+ 'Server certificate CA fingerprint does not match the value configured in ca_fingerprint'
200
+ end
201
+
202
+ connection.verified = true
203
+ end
204
+ end
205
+
206
+ def add_header(header)
207
+ headers = @arguments[:transport_options]&.[](:headers) || {}
208
+ headers.merge!(header)
209
+ @arguments[:transport_options].merge!(
210
+ headers: headers
211
+ )
212
+ end
213
+
175
214
  def add_header(header)
176
215
  headers = @arguments[:transport_options]&.[](:headers) || {}
177
216
  headers.merge!(header)
@@ -53,6 +53,7 @@ module Elastic
53
53
  @options = arguments[:options] || {}
54
54
  @options[:http] ||= {}
55
55
  @options[:retry_on_status] ||= []
56
+ @options[:delay_on_retry] ||= 0
56
57
 
57
58
  @block = block
58
59
  @compression = !!@options[:compression]
@@ -232,7 +233,7 @@ module Elastic
232
233
  # @api private
233
234
  #
234
235
  def __convert_to_json(o=nil, options={})
235
- o = o.is_a?(String) ? o : serializer.dump(o, options)
236
+ o.is_a?(String) ? o : serializer.dump(o, options)
236
237
  end
237
238
 
238
239
  # Returns a full URL based on information from host
@@ -273,6 +274,7 @@ module Elastic
273
274
  start = Time.now
274
275
  tries = 0
275
276
  reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
277
+ delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
276
278
 
277
279
  max_retries = if opts.key?(:retry_on_failure)
278
280
  opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
@@ -281,10 +283,10 @@ module Elastic
281
283
  end
282
284
 
283
285
  params = params.clone
284
-
285
286
  ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
286
287
 
287
288
  begin
289
+ sleep(delay_on_retry / 1000.0) if tries > 0
288
290
  tries += 1
289
291
  connection = get_connection or raise Error.new('Cannot get new connection from pool.')
290
292
 
@@ -293,14 +295,11 @@ module Elastic
293
295
  end
294
296
 
295
297
  url = connection.full_url(path, params)
296
-
297
298
  response = block.call(connection, url)
298
-
299
299
  connection.healthy! if connection.failures > 0
300
300
 
301
301
  # Raise an exception so we can catch it for `retry_on_status`
302
302
  __raise_transport_error(response) if response.status.to_i >= 300 && @retry_on_status.include?(response.status.to_i)
303
-
304
303
  rescue Elastic::Transport::Transport::ServerError => e
305
304
  if response && @retry_on_status.include?(response.status)
306
305
  log_warn "[#{e.class}] Attempt #{tries} to get response from #{url}"
@@ -313,7 +312,6 @@ module Elastic
313
312
  else
314
313
  raise e
315
314
  end
316
-
317
315
  rescue *host_unreachable_exceptions => e
318
316
  log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
319
317
 
@@ -335,37 +333,27 @@ module Elastic
335
333
  else
336
334
  raise e
337
335
  end
338
-
339
336
  rescue Exception => e
340
337
  log_fatal "[#{e.class}] #{e.message} (#{connection.host.inspect if connection})"
341
338
  raise e
342
-
343
339
  end #/begin
344
340
 
345
341
  duration = Time.now - start
346
342
 
347
343
  if response.status.to_i >= 300
348
- __log_response method, path, params, body, url, response, nil, 'N/A', duration
349
- __trace method, path, params, connection.connection.headers, body, url, response, nil, 'N/A', duration if tracer
344
+ __log_response(method, path, params, body, url, response, nil, 'N/A', duration)
345
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
350
346
 
351
347
  # Log the failure only when `ignore` doesn't match the response status
352
- unless ignore.include?(response.status.to_i)
353
- log_fatal "[#{response.status}] #{response.body}"
354
- end
355
-
348
+ log_fatal("[#{response.status}] #{response.body}") unless ignore.include?(response.status.to_i)
356
349
  __raise_transport_error response unless ignore.include?(response.status.to_i)
357
350
  end
358
351
 
359
352
  json = serializer.load(response.body) if response.body && !response.body.empty? && response.headers && response.headers["content-type"] =~ /json/
360
353
  took = (json['took'] ? sprintf('%.3fs', json['took']/1000.0) : 'n/a') rescue 'n/a'
361
-
362
- unless ignore.include?(response.status.to_i)
363
- __log_response method, path, params, body, url, response, json, took, duration
364
- end
365
-
366
- __trace method, path, params, connection.connection.headers, body, url, response, nil, 'N/A', duration if tracer
367
-
368
- warnings(response.headers['warning']) if response.headers&.[]('warning')
354
+ __log_response(method, path, params, body, url, response, json, took, duration) unless ignore.include?(response.status.to_i)
355
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
356
+ log_warn(response.headers['warning']) if response.headers&.[]('warning')
369
357
 
370
358
  Response.new response.status, json || response.body, response.headers
371
359
  ensure
@@ -385,17 +373,38 @@ module Elastic
385
373
 
386
374
  USER_AGENT_STR = 'User-Agent'.freeze
387
375
  USER_AGENT_REGEX = /user\-?\_?agent/
376
+ ACCEPT_ENCODING = 'Accept-Encoding'.freeze
377
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
388
378
  CONTENT_TYPE_STR = 'Content-Type'.freeze
389
379
  CONTENT_TYPE_REGEX = /content\-?\_?type/
390
380
  DEFAULT_CONTENT_TYPE = 'application/json'.freeze
391
381
  GZIP = 'gzip'.freeze
392
- ACCEPT_ENCODING = 'Accept-Encoding'.freeze
393
382
  GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
394
383
  HEX_STRING_DIRECTIVE = 'H*'.freeze
395
384
  RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
396
385
 
386
+ def compress_request(body, headers)
387
+ if body
388
+ headers ||= {}
389
+
390
+ if gzipped?(body)
391
+ headers[CONTENT_ENCODING] = GZIP
392
+ elsif use_compression?
393
+ headers[CONTENT_ENCODING] = GZIP
394
+ gzip = Zlib::GzipWriter.new(StringIO.new)
395
+ gzip << body
396
+ body = gzip.close.string
397
+ else
398
+ headers.delete(CONTENT_ENCODING)
399
+ end
400
+ elsif headers
401
+ headers.delete(CONTENT_ENCODING)
402
+ end
403
+
404
+ [body, headers]
405
+ end
406
+
397
407
  def decompress_response(body)
398
- return body unless use_compression?
399
408
  return body unless gzipped?(body)
400
409
 
401
410
  io = StringIO.new(body)
@@ -408,6 +417,8 @@ module Elastic
408
417
  end
409
418
 
410
419
  def gzipped?(body)
420
+ return unless body && !body.empty?
421
+
411
422
  body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
412
423
  end
413
424
 
@@ -441,8 +452,12 @@ module Elastic
441
452
  end
442
453
  end
443
454
 
444
- def warnings(warning)
445
- warn("warning: #{warning}")
455
+ def connection_headers(connection)
456
+ if defined?(Elastic::Transport::Transport::HTTP::Manticore) && self.class == Elastic::Transport::Transport::HTTP::Manticore
457
+ @request_options[:headers]
458
+ else
459
+ connection.connection.headers
460
+ end
446
461
  end
447
462
  end
448
463
  end
@@ -33,6 +33,7 @@ module Elastic
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 Elastic
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
@@ -25,23 +25,23 @@ module Elastic
25
25
  #
26
26
  class Curb
27
27
  include Base
28
-
29
28
  # Performs the request by invoking {Transport::Base#perform_request} with a block.
30
29
  #
31
30
  # @return [Response]
32
31
  # @see Transport::Base#perform_request
33
32
  #
34
33
  def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
35
- super do |connection, url|
34
+ super do |connection, _url|
36
35
  connection.connection.url = connection.full_url(path, params)
36
+ body = body ? __convert_to_json(body) : nil
37
+ body, headers = compress_request(body, headers)
37
38
 
38
39
  case method
39
40
  when 'HEAD'
40
41
  connection.connection.set :nobody, true
41
42
  when 'GET', 'POST', 'PUT', 'DELETE'
42
43
  connection.connection.set :nobody, false
43
-
44
- connection.connection.put_data = __convert_to_json(body) if body
44
+ connection.connection.put_data = body if body
45
45
 
46
46
  if headers
47
47
  if connection.connection.headers
@@ -43,11 +43,15 @@ module Elastic
43
43
  else
44
44
  headers
45
45
  end
46
+ body = body ? __convert_to_json(body) : nil
47
+ body, headers = compress_request(body, headers)
46
48
 
47
- response = connection.connection.run_request(method.downcase.to_sym,
48
- url,
49
- ( body ? __convert_to_json(body) : nil ),
50
- headers)
49
+ response = connection.connection.run_request(
50
+ method.downcase.to_sym,
51
+ url,
52
+ body,
53
+ headers
54
+ )
51
55
 
52
56
  Response.new response.status, decompress_response(response.body), response.headers
53
57
  end
@@ -60,7 +64,7 @@ module Elastic
60
64
  def __build_connection(host, options={}, block=nil)
61
65
  client = ::Faraday.new(__full_url(host), options, &block)
62
66
  apply_headers(client, options)
63
- Connections::Connection.new :host => host, :connection => client
67
+ Connections::Connection.new(host: host, connection: client)
64
68
  end
65
69
 
66
70
  # Returns an array of implementation specific connection errors.
@@ -83,7 +83,10 @@ module Elastic
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 Elastic
19
19
  module Transport
20
- VERSION = '8.0.0.pre1'.freeze
20
+ VERSION = '8.0.0.pre2'.freeze
21
21
  end
22
22
  end
@@ -20,6 +20,7 @@ require 'multi_json'
20
20
  require 'time'
21
21
  require 'timeout'
22
22
  require 'uri'
23
+ require 'zlib'
23
24
 
24
25
  require 'elastic/transport/transport/loggable'
25
26
  require 'elastic/transport/transport/serializer/multi_json'
@@ -1172,28 +1172,19 @@ describe Elastic::Transport::Client do
1172
1172
  end
1173
1173
 
1174
1174
  context 'when Elasticsearch response includes a warning header' do
1175
+ let(:logger) { double('logger', warn: '', warn?: '', info?: '', info: '', debug?: '', debug: '') }
1175
1176
  let(:client) do
1176
- Elastic::Transport::Client.new(hosts: hosts)
1177
+ Elastic::Transport::Client.new(hosts: hosts, logger: logger)
1177
1178
  end
1178
1179
 
1179
1180
  let(:warning) { 'Elasticsearch warning: "deprecation warning"' }
1180
1181
 
1181
1182
  it 'prints a warning' do
1182
- allow_any_instance_of(Elastic::Transport::Transport::Response).to receive(:headers) do
1183
- { 'warning' => warning }
1184
- end
1185
-
1186
- begin
1187
- stderr = $stderr
1188
- fake_stderr = StringIO.new
1189
- $stderr = fake_stderr
1190
-
1191
- client.perform_request('GET', '/')
1192
- fake_stderr.rewind
1193
- expect(fake_stderr.string).to eq("warning: #{warning}\n")
1194
- ensure
1195
- $stderr = stderr
1183
+ expect_any_instance_of(Faraday::Connection).to receive(:run_request) do
1184
+ Elastic::Transport::Transport::Response.new(200, {}, { 'warning' => warning })
1196
1185
  end
1186
+ client.perform_request('GET', '/')
1187
+ expect(logger).to have_received(:warn).with(warning)
1197
1188
  end
1198
1189
  end
1199
1190
  end
@@ -1337,6 +1328,29 @@ describe Elastic::Transport::Client do
1337
1328
  end
1338
1329
  end
1339
1330
 
1331
+ context 'when retry_on_failure is true and delay_on_retry is specified' do
1332
+ context 'when a node is unreachable' do
1333
+ let(:hosts) do
1334
+ [ELASTICSEARCH_HOSTS.first, "foobar1", "foobar2"]
1335
+ end
1336
+
1337
+ let(:options) do
1338
+ { retry_on_failure: true, delay_on_retry: 3000 }
1339
+ end
1340
+
1341
+ let(:responses) do
1342
+ 5.times.collect do
1343
+ client.perform_request('GET', '_nodes/_local')
1344
+ end
1345
+ end
1346
+
1347
+ it 'retries on failure' do
1348
+ allow_any_instance_of(Object).to receive(:sleep).with(3000 / 1000)
1349
+ expect(responses.all? { true }).to be(true)
1350
+ end
1351
+ end
1352
+ end
1353
+
1340
1354
  context 'when reload_on_failure is true' do
1341
1355
  let(:hosts) do
1342
1356
  [ELASTICSEARCH_HOSTS.first, 'foobar1', 'foobar2']
@@ -1391,7 +1405,7 @@ describe Elastic::Transport::Client do
1391
1405
  end
1392
1406
 
1393
1407
  it 'sets the Accept-Encoding header' do
1394
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1408
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1395
1409
  end
1396
1410
 
1397
1411
  it 'preserves the other headers' do
@@ -1409,7 +1423,7 @@ describe Elastic::Transport::Client do
1409
1423
  end
1410
1424
 
1411
1425
  it 'sets the Accept-Encoding header' do
1412
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1426
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1413
1427
  end
1414
1428
 
1415
1429
  it 'preserves the other headers' do
@@ -1427,7 +1441,7 @@ describe Elastic::Transport::Client do
1427
1441
  end
1428
1442
 
1429
1443
  it 'sets the Accept-Encoding header' do
1430
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1444
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1431
1445
  end
1432
1446
 
1433
1447
  it 'preserves the other headers' do
@@ -1445,7 +1459,7 @@ describe Elastic::Transport::Client do
1445
1459
  end
1446
1460
 
1447
1461
  it 'sets the Accept-Encoding header' do
1448
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1462
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1449
1463
  end
1450
1464
 
1451
1465
  it 'preserves the other headers' do
@@ -1463,7 +1477,7 @@ describe Elastic::Transport::Client do
1463
1477
  end
1464
1478
 
1465
1479
  it 'sets the Accept-Encoding header' do
1466
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1480
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1467
1481
  end
1468
1482
 
1469
1483
  it 'preserves the other headers' do
@@ -1485,7 +1499,7 @@ describe Elastic::Transport::Client do
1485
1499
  end
1486
1500
 
1487
1501
  it 'sets the Accept-Encoding header' do
1488
- expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1502
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding']).to eq 'gzip'
1489
1503
  end
1490
1504
 
1491
1505
  it 'preserves the other headers' do
@@ -1648,4 +1662,71 @@ describe Elastic::Transport::Client do
1648
1662
  end
1649
1663
  end
1650
1664
  end
1665
+
1666
+ context 'CA Fingerprinting' do
1667
+ context 'when setting a ca_fingerprint' do
1668
+ let(:certificate) do
1669
+ system(
1670
+ 'openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj "/C=BE/O=Test/CN=Test"' \
1671
+ ' -keyout certificate.key -out certificate.crt',
1672
+ err: File::NULL
1673
+ )
1674
+ OpenSSL::X509::Certificate.new File.read('./certificate.crt')
1675
+ end
1676
+
1677
+ let(:client) do
1678
+ Elastic::Transport::Client.new(
1679
+ host: 'https://elastic:changeme@localhost:9200',
1680
+ ca_fingerprint: OpenSSL::Digest::SHA256.hexdigest(certificate.to_der)
1681
+ )
1682
+ end
1683
+
1684
+ it 'validates CA fingerprints on perform request' do
1685
+ expect(client.transport.connections.connections.map(&:verified).uniq).to eq [false]
1686
+ allow(client.transport).to receive(:perform_request) { 'Hello' }
1687
+
1688
+ server = double('server').as_null_object
1689
+ allow(TCPSocket).to receive(:new) { server }
1690
+ socket = double('socket')
1691
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new) { socket }
1692
+ allow(socket).to receive(:connect) { nil }
1693
+ allow(socket).to receive(:peer_cert_chain) { [certificate] }
1694
+
1695
+ response = client.perform_request('GET', '/')
1696
+ expect(client.transport.connections.connections.map(&:verified).uniq).to eq [true]
1697
+ expect(response).to eq 'Hello'
1698
+ end
1699
+ end
1700
+
1701
+ context 'when using an http host' do
1702
+ let(:client) do
1703
+ Elastic::Transport::Client.new(
1704
+ host: 'http://elastic:changeme@localhost:9200',
1705
+ ca_fingerprint: 'test'
1706
+ )
1707
+ end
1708
+
1709
+ it 'raises an error' do
1710
+ expect do
1711
+ client.perform_request('GET', '/')
1712
+ end.to raise_exception(Elastic::Transport::Transport::Error)
1713
+ end
1714
+ end
1715
+
1716
+ context 'when not setting a ca_fingerprint' do
1717
+ let(:client) do
1718
+ Elastic::Transport::Client.new(
1719
+ host: 'http://elastic:changeme@localhost:9200'
1720
+ )
1721
+ end
1722
+
1723
+ it 'has unvalidated connections' do
1724
+ allow(client).to receive(:validate_ca_fingerprints) { nil }
1725
+ allow(client.transport).to receive(:perform_request) { nil }
1726
+
1727
+ client.perform_request('GET', '/')
1728
+ expect(client).to_not have_received(:validate_ca_fingerprints)
1729
+ end
1730
+ end
1731
+ end
1651
1732
  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 Elastic::Transport::Transport::HTTP::Curb do
22
+ let(:client) do
23
+ Elastic::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(Elastic::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
+ Elastic::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 Elastic::Transport::Transport::HTTP::Faraday do
21
+ let(:client) do
22
+ Elastic::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(Elastic::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
+ Elastic::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,146 @@
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 Elastic::Transport::Transport::HTTP::Manticore do
22
+ let(:client) do
23
+ Elastic::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(Elastic::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(
60
+ 'http://localhost:9200/',
61
+ { body: body, headers: expected_headers }
62
+ ).and_return(response)
63
+ perform_request
64
+ end
65
+
66
+ context 'when body nil' do
67
+ let(:body) { nil }
68
+ let(:request_params) { ['http://localhost:9200/', { body: body, headers: expected_headers }] }
69
+
70
+ it 'convert body to json' do
71
+ expect(client.transport).not_to receive(:__convert_to_json)
72
+ perform_request
73
+ end
74
+
75
+ it 'call compress_request' do
76
+ expect(client.transport).to receive(:compress_request).with(body, expected_headers)
77
+ perform_request
78
+ end
79
+
80
+ it 'run body with preper params' do
81
+ expect(
82
+ client.transport.connections.first.connection
83
+ ).to receive(:post).with('http://localhost:9200/', { headers: expected_headers }).and_return(response)
84
+ perform_request
85
+ end
86
+ end
87
+
88
+ context 'when body is hash' do
89
+ let(:body) { { foo: 'bar' } }
90
+ let(:body_string) { '{"foo":"bar"}' }
91
+ let(:request_params) { ['http://localhost:9200/', { body: body_string, headers: expected_headers }] }
92
+
93
+ it 'convert body to json' do
94
+ expect(client.transport).to receive(:__convert_to_json).with(body)
95
+ perform_request
96
+ end
97
+
98
+ it 'call compress_request' do
99
+ expect(client.transport).to receive(:compress_request).with(body_string, expected_headers)
100
+ perform_request
101
+ end
102
+
103
+ it 'run body with preper params' do
104
+ expect(
105
+ client.transport.connections.first.connection
106
+ ).to receive(:post).with(*request_params).and_return(response)
107
+ perform_request
108
+ end
109
+ end
110
+
111
+ context 'when compression enabled' do
112
+ let(:client) do
113
+ Elastic::Transport::Client.new(transport_class: described_class, compression: true)
114
+ end
115
+ let(:body_string) { '{"foo":"bar"}' }
116
+ let(:expected_headers) { super().merge({ 'Content-Encoding' => 'gzip', 'Accept-Encoding' => 'gzip' }) }
117
+ let(:request_params) { ['http://localhost:9200/', { body: compressed_body, headers: expected_headers }] }
118
+ let(:compressed_body) do
119
+ gzip = Zlib::GzipWriter.new(StringIO.new)
120
+ gzip << body_string
121
+ gzip.close.string
122
+ end
123
+
124
+ it 'run body with preper params' do
125
+ expect(
126
+ client.transport.connections.first.connection
127
+ ).to receive(:post).with(*request_params).and_return(response)
128
+ perform_request
129
+ end
130
+
131
+ context 'when client makes second request with nil boby' do
132
+ before { perform_request }
133
+
134
+ it 'remove Content-Encoding header' do
135
+ expected_headers.delete('Content-Encoding')
136
+ expect(
137
+ client.transport.connections.first.connection
138
+ ).to receive(:post).with('http://localhost:9200/', { headers: expected_headers })
139
+ .and_return(response)
140
+ client.perform_request('POST', '/', {}, nil, headers)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,46 @@
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 'test_helper'
19
+
20
+ if JRUBY
21
+ require 'elastic/transport/transport/http/manticore'
22
+
23
+ class Elastic::Transport::ClientManticoreIntegrationTest < Minitest::Test
24
+ context "Transport" do
25
+ setup do
26
+ uri = URI(HOST)
27
+ @host = {
28
+ host: uri.host,
29
+ port: uri.port,
30
+ user: uri.user,
31
+ password: uri.password
32
+ }
33
+ end
34
+
35
+ should 'allow to customize the Faraday adapter to Manticore' do
36
+ client = Elastic::Transport::Client.new(
37
+ transport_class: Elastic::Transport::Transport::HTTP::Manticore,
38
+ trace: true,
39
+ hosts: [@host]
40
+ )
41
+ response = client.perform_request 'GET', ''
42
+ assert_respond_to(response.body, :to_hash)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -93,6 +93,6 @@ class Elastic::Transport::ClientIntegrationTest < Minitest::Test
93
93
  assert_respond_to(response.body, :to_hash)
94
94
  assert_not_nil response.body['name']
95
95
  assert_equal 'application/json', response.headers['content-type']
96
- end unless JRUBY
96
+ end unless jruby?
97
97
  end
98
98
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic-transport
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0.pre1
4
+ version: 8.0.0.pre2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karel Minarik
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-09-14 00:00:00.000000000 Z
13
+ date: 2021-10-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: multi_json
@@ -347,13 +347,17 @@ files:
347
347
  - lib/elastic/transport/transport/serializer/multi_json.rb
348
348
  - lib/elastic/transport/transport/sniffer.rb
349
349
  - lib/elastic/transport/version.rb
350
- - spec/elasticsearch/connections/collection_spec.rb
351
- - spec/elasticsearch/connections/selector_spec.rb
352
- - spec/elasticsearch/transport/base_spec.rb
353
- - spec/elasticsearch/transport/client_spec.rb
354
- - spec/elasticsearch/transport/meta_header_spec.rb
355
- - spec/elasticsearch/transport/sniffer_spec.rb
350
+ - spec/elastic/connections/collection_spec.rb
351
+ - spec/elastic/connections/selector_spec.rb
352
+ - spec/elastic/transport/base_spec.rb
353
+ - spec/elastic/transport/client_spec.rb
354
+ - spec/elastic/transport/http/curb_spec.rb
355
+ - spec/elastic/transport/http/faraday_spec.rb
356
+ - spec/elastic/transport/http/manticore_spec.rb
357
+ - spec/elastic/transport/meta_header_spec.rb
358
+ - spec/elastic/transport/sniffer_spec.rb
356
359
  - spec/spec_helper.rb
360
+ - test/integration/jruby_test.rb
357
361
  - test/integration/transport_test.rb
358
362
  - test/profile/client_benchmark_test.rb
359
363
  - test/test_helper.rb
@@ -393,13 +397,17 @@ signing_key:
393
397
  specification_version: 4
394
398
  summary: Low level Ruby client for Elastic services.
395
399
  test_files:
396
- - spec/elasticsearch/connections/collection_spec.rb
397
- - spec/elasticsearch/connections/selector_spec.rb
398
- - spec/elasticsearch/transport/base_spec.rb
399
- - spec/elasticsearch/transport/client_spec.rb
400
- - spec/elasticsearch/transport/meta_header_spec.rb
401
- - spec/elasticsearch/transport/sniffer_spec.rb
400
+ - spec/elastic/connections/collection_spec.rb
401
+ - spec/elastic/connections/selector_spec.rb
402
+ - spec/elastic/transport/base_spec.rb
403
+ - spec/elastic/transport/client_spec.rb
404
+ - spec/elastic/transport/http/curb_spec.rb
405
+ - spec/elastic/transport/http/faraday_spec.rb
406
+ - spec/elastic/transport/http/manticore_spec.rb
407
+ - spec/elastic/transport/meta_header_spec.rb
408
+ - spec/elastic/transport/sniffer_spec.rb
402
409
  - spec/spec_helper.rb
410
+ - test/integration/jruby_test.rb
403
411
  - test/integration/transport_test.rb
404
412
  - test/profile/client_benchmark_test.rb
405
413
  - test/test_helper.rb