elasticsearch-transport 7.13.3 → 7.17.7

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