elasticsearch-transport 7.13.3 → 7.17.7

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: a045f07e51e54095cdd4d050a786070b35eadc7356b5167c99da881738cf5607
4
- data.tar.gz: 854f2d899cdbbb184ce945a6b6dfa0a1f7f463bcf3d818c4e7f66a28ca81a504
3
+ metadata.gz: 515176cfbf829f3153d85e4975eebed91424aaa14256d332f0dc3f87030ce671
4
+ data.tar.gz: c8bdcb22a2f81446f55d4d3025f05863020629eb0ff139e80832a3dc8f43eb8b
5
5
  SHA512:
6
- metadata.gz: 89c3b0674a119afb4aa397bacf6282bd301862ca4a5aa107b0789c93493644f678414587dd695e3210fdbe8afaed6bf9db8a10ee1a3597767211afb2fb4d4da4
7
- data.tar.gz: 6706d0c3441f9eed32b7e930e2cd808774bb8de1083109a6681a1433929dc407f324e97bdbaf1bf3dce9c452fc527abc3cbd7ccd071cfbff46977a93af31ba5d
6
+ metadata.gz: 850fc6102767844a698044cbc3cef9e63f01895ef5735e9c9e831db4e62dac9f448c71cc6eac591f5ce3e225b35106a083366ead9e3267d24f8723f81a792f8c
7
+ data.tar.gz: 18ba115ba8d1b55866806c75845c8a39dd920c9bd227b64e586ecdedc7274530c28dcc3f62bfb70f61510091f2acc3aa67511323e0b6b70c5afdbf5df035f175
data/Gemfile CHANGED
@@ -20,16 +20,12 @@ source 'https://rubygems.org'
20
20
  # Specify your gem's dependencies in elasticsearch-transport.gemspec
21
21
  gemspec
22
22
 
23
- if File.exist? File.expand_path('../../elasticsearch-api/elasticsearch-api.gemspec', __FILE__)
24
- gem 'elasticsearch-api', path: File.expand_path('../../elasticsearch-api', __FILE__), require: false
23
+ if File.exist? File.expand_path('../elasticsearch-api/elasticsearch-api.gemspec', __dir__)
24
+ gem 'elasticsearch-api', path: File.expand_path('../elasticsearch-api', __dir__), require: false
25
25
  end
26
26
 
27
- if File.exist? File.expand_path('../../elasticsearch-extensions/elasticsearch-extensions.gemspec', __FILE__)
28
- gem 'elasticsearch-extensions', path: File.expand_path('../../elasticsearch-extensions', __FILE__), require: false
29
- end
30
-
31
- if File.exist? File.expand_path('../../elasticsearch/elasticsearch.gemspec', __FILE__)
32
- gem 'elasticsearch', path: File.expand_path('../../elasticsearch', __FILE__), require: false
27
+ if File.exist? File.expand_path('../elasticsearch/elasticsearch.gemspec', __dir__)
28
+ gem 'elasticsearch', path: File.expand_path('../elasticsearch', __dir__), require: false
33
29
  end
34
30
 
35
31
  group :development, :test do
data/README.md CHANGED
@@ -424,10 +424,7 @@ To configure the _Faraday_ instance directly, use a block:
424
424
  f.adapter :patron
425
425
  end
426
426
 
427
- You can use any standard Faraday middleware and plugins in the configuration block, for example sign the requests for the [AWS Elasticsearch service](https://aws.amazon.com/elasticsearch-service/). See [the AWS documentation](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-request-signing.html#es-request-signing-ruby) for an example.
428
-
429
- You can also initialize the transport class yourself, and pass it to the client constructor
430
- as the `transport` argument:
427
+ You can use any standard Faraday middleware and plugins in the configuration block. You can also initialize the transport class yourself, and pass it to the client constructor as the `transport` argument:
431
428
 
432
429
  ```ruby
433
430
  require 'patron'
@@ -561,16 +558,14 @@ Github's pull requests and issues are used to communicate, send bug reports and
561
558
  To work on the code, clone and bootstrap the main repository first --
562
559
  please see instructions in the main [README](../README.md#development).
563
560
 
564
- To run tests, launch a testing cluster -- again, see instructions
565
- in the main [README](../README.md#development) -- and use the Rake tasks:
561
+ To run tests, launch a testing cluster and use the Rake tasks:
566
562
 
567
563
  ```
568
564
  time rake test:unit
569
565
  time rake test:integration
570
566
  ```
571
567
 
572
- Unit tests have to use Ruby 1.8 compatible syntax, integration tests
573
- can use Ruby 2.x syntax and features.
568
+ Use `COVERAGE=true` before running a test task to check coverage with Simplecov.
574
569
 
575
570
  ## License
576
571
 
data/Rakefile CHANGED
@@ -27,38 +27,37 @@ require 'rake/testtask'
27
27
  require 'rspec/core/rake_task'
28
28
 
29
29
  namespace :test do
30
-
31
- desc "Wait for Elasticsearch to be in a green state"
30
+ desc 'Wait for Elasticsearch to be in a green state'
32
31
  task :wait_for_green do
33
32
  sh '../scripts/wait-cluster.sh'
34
33
  end
35
34
 
36
- task :spec => :wait_for_green
37
35
  RSpec::Core::RakeTask.new(:spec)
38
36
 
39
37
  Rake::TestTask.new(:unit) do |test|
40
38
  test.libs << 'lib' << 'test'
41
- test.test_files = FileList["test/unit/**/*_test.rb"]
39
+ test.test_files = FileList['test/unit/**/*_test.rb']
42
40
  test.verbose = false
43
41
  test.warning = false
44
42
  end
45
43
 
46
44
  Rake::TestTask.new(:integration) do |test|
47
45
  test.libs << 'lib' << 'test'
48
- test.test_files = FileList["test/integration/**/*_test.rb"]
49
- test.deps = [ 'test:wait_for_green', 'test:spec' ]
46
+ test.test_files = FileList['test/integration/**/*_test.rb']
47
+ test.deps = ['test:wait_for_green', 'test:spec']
50
48
  test.verbose = false
51
49
  test.warning = false
52
50
  end
53
51
 
54
- Rake::TestTask.new(:all) do |test|
55
- test.libs << 'lib' << 'test'
56
- test.test_files = FileList["test/unit/**/*_test.rb", "test/integration/**/*_test.rb"]
52
+ desc 'Run all tests'
53
+ task :all do
54
+ Rake::Task['test:unit'].invoke
55
+ Rake::Task['test:integration'].invoke
57
56
  end
58
57
 
59
58
  Rake::TestTask.new(:profile) do |test|
60
59
  test.libs << 'lib' << 'test'
61
- test.test_files = FileList["test/profile/**/*_test.rb"]
60
+ test.test_files = FileList['test/profile/**/*_test.rb']
62
61
  end
63
62
 
64
63
  namespace :cluster do
@@ -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($/)
@@ -47,29 +47,29 @@ Gem::Specification.new do |s|
47
47
  s.add_dependency 'multi_json'
48
48
  s.add_dependency 'faraday', '~> 1'
49
49
 
50
+ s.add_development_dependency 'ansi'
51
+ s.add_development_dependency 'bundler'
50
52
  s.add_development_dependency 'cane'
51
- s.add_development_dependency 'curb' unless defined? JRUBY_VERSION
53
+ s.add_development_dependency 'curb' unless defined? JRUBY_VERSION
54
+ s.add_development_dependency 'elasticsearch', ['>= 7', '< 8.0.0']
52
55
  s.add_development_dependency 'elasticsearch-extensions'
53
- s.add_development_dependency 'minitest'
54
- s.add_development_dependency 'minitest-reporters'
55
- s.add_development_dependency 'rake', '~> 13'
56
- s.add_development_dependency 'require-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
57
- s.add_development_dependency 'ruby-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
58
- s.add_development_dependency 'simplecov', '~> 0.17', '< 0.18'
59
- s.add_development_dependency 'simplecov-rcov'
60
- s.add_development_dependency 'ansi'
61
56
  s.add_development_dependency 'hashie'
62
57
  s.add_development_dependency 'httpclient'
63
- s.add_development_dependency 'manticore', '~> 0.6' if defined? JRUBY_VERSION
58
+ s.add_development_dependency 'manticore' if defined? JRUBY_VERSION
59
+ s.add_development_dependency 'minitest'
60
+ s.add_development_dependency 'minitest-reporters'
64
61
  s.add_development_dependency 'mocha'
65
62
  s.add_development_dependency 'net-http-persistent'
66
63
  s.add_development_dependency 'patron' unless defined? JRUBY_VERSION
67
64
  s.add_development_dependency 'pry'
65
+ s.add_development_dependency 'rake', '~> 13'
66
+ s.add_development_dependency 'require-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
67
+ s.add_development_dependency 'ruby-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
68
68
  s.add_development_dependency 'shoulda-context'
69
+ s.add_development_dependency 'simplecov'
69
70
  s.add_development_dependency 'test-unit', '~> 2'
70
71
  s.add_development_dependency 'typhoeus', '~> 1.4'
71
72
  s.add_development_dependency 'yard'
72
- s.add_development_dependency 'bundler'
73
73
 
74
74
  s.description = <<-DESC.gsub(/^ /, '')
75
75
  Ruby client for Elasticsearch. See the `elasticsearch` gem for full integration.
@@ -20,14 +20,13 @@ require 'elasticsearch/transport/meta_header'
20
20
 
21
21
  module Elasticsearch
22
22
  module Transport
23
-
24
23
  # Handles communication with an Elasticsearch cluster.
25
24
  #
26
25
  # See {file:README.md README} for usage and code examples.
27
26
  #
28
27
  class Client
29
28
  include MetaHeader
30
- DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday
29
+ DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday
31
30
 
32
31
  DEFAULT_LOGGER = lambda do
33
32
  require 'logger'
@@ -93,6 +92,8 @@ module Elasticsearch
93
92
  #
94
93
  # @option arguments [Boolean,Number] :retry_on_failure Retry X times when request fails before raising and
95
94
  # exception (false by default)
95
+ # @option arguments [Number] :delay_on_retry Delay in milliseconds between each retry (0 by default)
96
+ #
96
97
  # @option arguments Array<Number> :retry_on_status Retry when specific status codes are returned
97
98
  #
98
99
  # @option arguments [Boolean] :reload_on_failure Reload connections after failure (false by default)
@@ -127,6 +128,7 @@ module Elasticsearch
127
128
  # if you're using X-Opaque-Id
128
129
  # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
129
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
130
132
  #
131
133
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
132
134
  #
@@ -137,6 +139,7 @@ module Elasticsearch
137
139
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
138
140
  @arguments[:reload_connections] ||= false
139
141
  @arguments[:retry_on_failure] ||= false
142
+ @arguments[:delay_on_retry] ||= 0
140
143
  @arguments[:reload_on_failure] ||= false
141
144
  @arguments[:randomize_hosts] ||= false
142
145
  @arguments[:transport_options] ||= {}
@@ -157,6 +160,7 @@ module Elasticsearch
157
160
 
158
161
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
159
162
  @opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
163
+ @ca_fingerprint = @arguments.delete(:ca_fingerprint)
160
164
 
161
165
  if @arguments[:request_timeout]
162
166
  @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
@@ -189,6 +193,7 @@ module Elasticsearch
189
193
  opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
190
194
  headers.merge!('X-Opaque-Id' => opaque_id)
191
195
  end
196
+ validate_ca_fingerprints if @ca_fingerprint
192
197
  transport.perform_request(method, path, params, body, headers)
193
198
  end
194
199
 
@@ -203,15 +208,41 @@ module Elasticsearch
203
208
 
204
209
  def set_compatibility_header
205
210
  return unless ['1', 'true'].include?(ENV['ELASTIC_CLIENT_APIVERSIONING'])
211
+ return if instance_variable_get('@options').dig(:transport_options, :headers, 'Accept')
206
212
 
207
213
  add_header(
208
214
  {
209
- 'Accept' => 'application/vnd.elasticsearch+json;compatible-with=7',
215
+ 'Accept' => 'application/vnd.elasticsearch+json; compatible-with=7',
210
216
  'Content-Type' => 'application/vnd.elasticsearch+json; compatible-with=7'
211
217
  }
212
218
  )
213
219
  end
214
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
+
215
246
  def add_header(header)
216
247
  headers = @arguments[:transport_options]&.[](:headers) || {}
217
248
  headers.merge!(header)
@@ -18,7 +18,6 @@
18
18
  module Elasticsearch
19
19
  module Transport
20
20
  module Transport
21
-
22
21
  # @abstract Module with common functionality for transport implementations.
23
22
  #
24
23
  module Base
@@ -54,6 +53,7 @@ module Elasticsearch
54
53
  @options = arguments[:options] || {}
55
54
  @options[:http] ||= {}
56
55
  @options[:retry_on_status] ||= []
56
+ @options[:delay_on_retry] ||= 0
57
57
 
58
58
  @block = block
59
59
  @compression = !!@options[:compression]
@@ -223,7 +223,7 @@ module Elasticsearch
223
223
  # @api private
224
224
  #
225
225
  def __convert_to_json(o=nil, options={})
226
- o = o.is_a?(String) ? o : serializer.dump(o, options)
226
+ o.is_a?(String) ? o : serializer.dump(o, options)
227
227
  end
228
228
 
229
229
  # Returns a full URL based on information from host
@@ -264,6 +264,7 @@ module Elasticsearch
264
264
  start = Time.now
265
265
  tries = 0
266
266
  reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
267
+ delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
267
268
 
268
269
  max_retries = if opts.key?(:retry_on_failure)
269
270
  opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
@@ -272,10 +273,10 @@ module Elasticsearch
272
273
  end
273
274
 
274
275
  params = params.clone
275
-
276
276
  ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
277
277
 
278
278
  begin
279
+ sleep(delay_on_retry / 1000.0) if tries > 0
279
280
  tries += 1
280
281
  connection = get_connection or raise Error.new('Cannot get new connection from pool.')
281
282
 
@@ -284,9 +285,7 @@ module Elasticsearch
284
285
  end
285
286
 
286
287
  url = connection.full_url(path, params)
287
-
288
288
  response = block.call(connection, url)
289
-
290
289
  connection.healthy! if connection.failures > 0
291
290
 
292
291
  # Raise an exception so we can catch it for `retry_on_status`
@@ -309,7 +308,6 @@ module Elasticsearch
309
308
  log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
310
309
 
311
310
  connection.dead!
312
-
313
311
  if reload_on_failure and tries < connections.all.size
314
312
  log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
315
313
  reload_connections! and retry
@@ -336,14 +334,10 @@ module Elasticsearch
336
334
  duration = Time.now - start
337
335
 
338
336
  if response.status.to_i >= 300
339
- __log_response method, path, params, body, url, response, nil, 'N/A', duration
340
- __trace method, path, params, connection.connection.headers, body, url, response, nil, 'N/A', duration if tracer
341
-
337
+ __log_response(method, path, params, body, url, response, nil, 'N/A', duration)
338
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
342
339
  # Log the failure only when `ignore` doesn't match the response status
343
- unless ignore.include?(response.status.to_i)
344
- log_fatal "[#{response.status}] #{response.body}"
345
- end
346
-
340
+ log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i)
347
341
  __raise_transport_error response unless ignore.include?(response.status.to_i)
348
342
  end
349
343
 
@@ -354,10 +348,8 @@ module Elasticsearch
354
348
  __log_response method, path, params, body, url, response, json, took, duration
355
349
  end
356
350
 
357
- __trace method, path, params, connection.connection.headers, body, url, response, nil, 'N/A', duration if tracer
358
-
359
- warnings(response.headers['warning']) if response.headers&.[]('warning')
360
-
351
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
352
+ log_warn(response.headers['warning']) if response.headers&.[]('warning')
361
353
  Response.new response.status, json || response.body, response.headers
362
354
  ensure
363
355
  @last_request_at = Time.now
@@ -376,17 +368,38 @@ module Elasticsearch
376
368
 
377
369
  USER_AGENT_STR = 'User-Agent'.freeze
378
370
  USER_AGENT_REGEX = /user\-?\_?agent/
371
+ ACCEPT_ENCODING = 'Accept-Encoding'.freeze
372
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
379
373
  CONTENT_TYPE_STR = 'Content-Type'.freeze
380
374
  CONTENT_TYPE_REGEX = /content\-?\_?type/
381
375
  DEFAULT_CONTENT_TYPE = 'application/json'.freeze
382
376
  GZIP = 'gzip'.freeze
383
- ACCEPT_ENCODING = 'Accept-Encoding'.freeze
384
377
  GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
385
378
  HEX_STRING_DIRECTIVE = 'H*'.freeze
386
379
  RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
387
380
 
381
+ def compress_request(body, headers)
382
+ if body
383
+ headers ||= {}
384
+
385
+ if gzipped?(body)
386
+ headers[CONTENT_ENCODING] = GZIP
387
+ elsif use_compression?
388
+ headers[CONTENT_ENCODING] = GZIP
389
+ gzip = Zlib::GzipWriter.new(StringIO.new)
390
+ gzip << body
391
+ body = gzip.close.string
392
+ else
393
+ headers.delete(CONTENT_ENCODING)
394
+ end
395
+ elsif headers
396
+ headers.delete(CONTENT_ENCODING)
397
+ end
398
+
399
+ [body, headers]
400
+ end
401
+
388
402
  def decompress_response(body)
389
- return body unless use_compression?
390
403
  return body unless gzipped?(body)
391
404
 
392
405
  io = StringIO.new(body)
@@ -399,6 +412,8 @@ module Elasticsearch
399
412
  end
400
413
 
401
414
  def gzipped?(body)
415
+ return unless body && !body.empty?
416
+
402
417
  body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
403
418
  end
404
419
 
@@ -415,7 +430,7 @@ module Elasticsearch
415
430
  end
416
431
 
417
432
  def find_value(hash, regex)
418
- key_value = hash.find { |k,v| k.to_s.downcase =~ regex }
433
+ key_value = hash.find { |k, _| k.to_s.downcase =~ regex }
419
434
  if key_value
420
435
  hash.delete(key_value[0])
421
436
  key_value[1]
@@ -432,8 +447,12 @@ module Elasticsearch
432
447
  end
433
448
  end
434
449
 
435
- def warnings(warning)
436
- warn("warning: #{warning}")
450
+ def connection_headers(connection)
451
+ if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore
452
+ @request_options[:headers]
453
+ else
454
+ connection.connection.headers
455
+ end
437
456
  end
438
457
  end
439
458
  end
@@ -33,6 +33,7 @@ module Elasticsearch
33
33
  DEFAULT_RESURRECT_TIMEOUT = 60
34
34
 
35
35
  attr_reader :host, :connection, :options, :failures, :dead_since
36
+ attr_accessor :verified
36
37
 
37
38
  # @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`)
38
39
  # @option arguments [Object] :connection The transport-specific physical connection or "session"
@@ -42,6 +43,7 @@ module Elasticsearch
42
43
  @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
43
44
  @connection = arguments[:connection]
44
45
  @options = arguments[:options] || {}
46
+ @verified = false
45
47
  @state_mutex = Mutex.new
46
48
 
47
49
  @options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT
@@ -153,7 +155,6 @@ module Elasticsearch
153
155
  "<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
154
156
  end
155
157
  end
156
-
157
158
  end
158
159
  end
159
160
  end
@@ -64,6 +64,7 @@ module Elasticsearch
64
64
  418 => 'ImATeapot',
65
65
  421 => 'TooManyConnectionsFromThisIP',
66
66
  426 => 'UpgradeRequired',
67
+ 429 => 'TooManyRequests',
67
68
  450 => 'BlockedByWindowsParentalControls',
68
69
  494 => 'RequestHeaderTooLarge',
69
70
  497 => 'HTTPToHTTPS',
@@ -19,51 +19,64 @@ 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
- when 'HEAD'
41
- connection.connection.set :nobody, true
42
- when 'GET', 'POST', 'PUT', 'DELETE'
43
- connection.connection.set :nobody, false
44
-
45
- connection.connection.put_data = __convert_to_json(body) if body
46
-
47
- if headers
48
- if connection.connection.headers
49
- connection.connection.headers.merge!(headers)
50
- else
51
- connection.connection.headers = headers
52
- end
40
+ when 'HEAD'
41
+ connection.connection.set :nobody, true
42
+ when 'GET', 'POST', 'PUT', 'DELETE'
43
+ connection.connection.set :nobody, false
44
+
45
+ connection.connection.put_data = body if body
46
+
47
+ if headers
48
+ if connection.connection.headers
49
+ connection.connection.headers.merge!(headers)
50
+ else
51
+ connection.connection.headers = headers
53
52
  end
54
-
55
- else raise ArgumentError, "Unsupported HTTP method: #{method}"
53
+ end
54
+ else
55
+ raise ArgumentError, "Unsupported HTTP method: #{method}"
56
56
  end
57
57
 
58
58
  connection.connection.http(method.to_sym)
59
59
 
60
- response_headers = {}
61
- response_headers['content-type'] = 'application/json' if connection.connection.header_str =~ /\/json/
60
+ Response.new(
61
+ connection.connection.response_code,
62
+ decompress_response(connection.connection.body_str),
63
+ headers(connection)
64
+ )
65
+ end
66
+ end
67
+
68
+ def headers(connection)
69
+ headers_string = connection.connection.header_str
70
+ return nil if headers_string.nil?
62
71
 
63
- Response.new connection.connection.response_code,
64
- decompress_response(connection.connection.body_str),
65
- response_headers
72
+ response_headers = headers_string&.split(/\\r\\n|\r\n/).reject(&:empty?)
73
+ response_headers.shift # Removes HTTP status string
74
+ processed_header = response_headers.flat_map { |s| s.scan(/^(\S+): (.+)/) }
75
+ headers_hash = Hash[processed_header].transform_keys(&:downcase)
76
+ if headers_hash['content-type']&.match?(/application\/json/)
77
+ headers_hash['content-type'] = 'application/json'
66
78
  end
79
+ headers_hash
67
80
  end
68
81
 
69
82
  # Builds and returns a connection
@@ -72,9 +85,8 @@ module Elasticsearch
72
85
  #
73
86
  def __build_connection(host, options={}, block=nil)
74
87
  client = ::Curl::Easy.new
75
-
76
88
  apply_headers(client, options)
77
- client.url = __full_url(host)
89
+ client.url = __full_url(host)
78
90
 
79
91
  if host[:user]
80
92
  client.http_auth_types = host[:auth_type] || :basic
@@ -106,13 +118,13 @@ module Elasticsearch
106
118
 
107
119
  def user_agent_header(client)
108
120
  @user_agent ||= begin
109
- meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
110
- if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
111
- meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
112
- end
113
- meta << "Curb #{Curl::CURB_VERSION}"
114
- "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
115
- end
121
+ meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
122
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
123
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
124
+ end
125
+ meta << "Curb #{Curl::CURB_VERSION}"
126
+ "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
127
+ end
116
128
  end
117
129
  end
118
130
  end
@@ -19,7 +19,6 @@ module Elasticsearch
19
19
  module Transport
20
20
  module Transport
21
21
  module HTTP
22
-
23
22
  # The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday)
24
23
  # library for abstracting the HTTP client.
25
24
  #
@@ -45,9 +44,12 @@ module Elasticsearch
45
44
  headers
46
45
  end
47
46
 
47
+ body = body ? __convert_to_json(body) : nil
48
+ body, headers = compress_request(body, headers)
49
+
48
50
  response = connection.connection.run_request(method.downcase.to_sym,
49
51
  url,
50
- ( body ? __convert_to_json(body) : nil ),
52
+ body,
51
53
  headers)
52
54
 
53
55
  Response.new response.status, decompress_response(response.body), response.headers
@@ -61,7 +63,7 @@ module Elasticsearch
61
63
  def __build_connection(host, options={}, block=nil)
62
64
  client = ::Faraday.new(__full_url(host), options, &block)
63
65
  apply_headers(client, options)
64
- Connections::Connection.new :host => host, :connection => client
66
+ Connections::Connection.new(host: host, connection: client)
65
67
  end
66
68
 
67
69
  # Returns an array of implementation specific connection errors.