elastic-transport 8.0.0.pre1 → 8.0.0.pre2

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: 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