elasticsearch-transport 7.13.3 → 7.17.0

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: 4fc7f318edc5730a123909637ce8cf74f32c4ddb2c9571e6744b4ad864e5c5d8
4
+ data.tar.gz: 11549daa6e4586a272f01e64ae56c01a510a57687fc0a06ecec58b35906f21d3
5
5
  SHA512:
6
- metadata.gz: 89c3b0674a119afb4aa397bacf6282bd301862ca4a5aa107b0789c93493644f678414587dd695e3210fdbe8afaed6bf9db8a10ee1a3597767211afb2fb4d4da4
7
- data.tar.gz: 6706d0c3441f9eed32b7e930e2cd808774bb8de1083109a6681a1433929dc407f324e97bdbaf1bf3dce9c452fc527abc3cbd7ccd071cfbff46977a93af31ba5d
6
+ metadata.gz: f5ea6b69fcb79dc9502b057195ef71bd6f60aca21170cae677e92510f01091199391ac38ce081af54fb269fa1600aeee81b586565317eac3065ff308c177f703
7
+ data.tar.gz: 9274659714937b8505a1a2252da28658900458982b3abe338838dbe8328e5408481351edb10623378a3e6f48cf8ca48a55a14f9739cc7e6e5650627b4a03a587
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)
@@ -54,6 +54,7 @@ module Elasticsearch
54
54
  @options = arguments[:options] || {}
55
55
  @options[:http] ||= {}
56
56
  @options[:retry_on_status] ||= []
57
+ @options[:delay_on_retry] ||= 0
57
58
 
58
59
  @block = block
59
60
  @compression = !!@options[:compression]
@@ -223,7 +224,7 @@ module Elasticsearch
223
224
  # @api private
224
225
  #
225
226
  def __convert_to_json(o=nil, options={})
226
- o = o.is_a?(String) ? o : serializer.dump(o, options)
227
+ o.is_a?(String) ? o : serializer.dump(o, options)
227
228
  end
228
229
 
229
230
  # Returns a full URL based on information from host
@@ -264,6 +265,7 @@ module Elasticsearch
264
265
  start = Time.now
265
266
  tries = 0
266
267
  reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
268
+ delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
267
269
 
268
270
  max_retries = if opts.key?(:retry_on_failure)
269
271
  opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
@@ -272,10 +274,10 @@ module Elasticsearch
272
274
  end
273
275
 
274
276
  params = params.clone
275
-
276
277
  ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
277
278
 
278
279
  begin
280
+ sleep(delay_on_retry / 1000.0) if tries > 0
279
281
  tries += 1
280
282
  connection = get_connection or raise Error.new('Cannot get new connection from pool.')
281
283
 
@@ -284,9 +286,7 @@ module Elasticsearch
284
286
  end
285
287
 
286
288
  url = connection.full_url(path, params)
287
-
288
289
  response = block.call(connection, url)
289
-
290
290
  connection.healthy! if connection.failures > 0
291
291
 
292
292
  # Raise an exception so we can catch it for `retry_on_status`
@@ -309,7 +309,6 @@ module Elasticsearch
309
309
  log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
310
310
 
311
311
  connection.dead!
312
-
313
312
  if reload_on_failure and tries < connections.all.size
314
313
  log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
315
314
  reload_connections! and retry
@@ -336,14 +335,10 @@ module Elasticsearch
336
335
  duration = Time.now - start
337
336
 
338
337
  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
-
338
+ __log_response(method, path, params, body, url, response, nil, 'N/A', duration)
339
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
342
340
  # 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
-
341
+ log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i)
347
342
  __raise_transport_error response unless ignore.include?(response.status.to_i)
348
343
  end
349
344
 
@@ -354,10 +349,8 @@ module Elasticsearch
354
349
  __log_response method, path, params, body, url, response, json, took, duration
355
350
  end
356
351
 
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
-
352
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
353
+ log_warn(response.headers['warning']) if response.headers&.[]('warning')
361
354
  Response.new response.status, json || response.body, response.headers
362
355
  ensure
363
356
  @last_request_at = Time.now
@@ -376,17 +369,38 @@ module Elasticsearch
376
369
 
377
370
  USER_AGENT_STR = 'User-Agent'.freeze
378
371
  USER_AGENT_REGEX = /user\-?\_?agent/
372
+ ACCEPT_ENCODING = 'Accept-Encoding'.freeze
373
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
379
374
  CONTENT_TYPE_STR = 'Content-Type'.freeze
380
375
  CONTENT_TYPE_REGEX = /content\-?\_?type/
381
376
  DEFAULT_CONTENT_TYPE = 'application/json'.freeze
382
377
  GZIP = 'gzip'.freeze
383
- ACCEPT_ENCODING = 'Accept-Encoding'.freeze
384
378
  GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
385
379
  HEX_STRING_DIRECTIVE = 'H*'.freeze
386
380
  RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
387
381
 
382
+ def compress_request(body, headers)
383
+ if body
384
+ headers ||= {}
385
+
386
+ if gzipped?(body)
387
+ headers[CONTENT_ENCODING] = GZIP
388
+ elsif use_compression?
389
+ headers[CONTENT_ENCODING] = GZIP
390
+ gzip = Zlib::GzipWriter.new(StringIO.new)
391
+ gzip << body
392
+ body = gzip.close.string
393
+ else
394
+ headers.delete(CONTENT_ENCODING)
395
+ end
396
+ elsif headers
397
+ headers.delete(CONTENT_ENCODING)
398
+ end
399
+
400
+ [body, headers]
401
+ end
402
+
388
403
  def decompress_response(body)
389
- return body unless use_compression?
390
404
  return body unless gzipped?(body)
391
405
 
392
406
  io = StringIO.new(body)
@@ -399,6 +413,8 @@ module Elasticsearch
399
413
  end
400
414
 
401
415
  def gzipped?(body)
416
+ return unless body && !body.empty?
417
+
402
418
  body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
403
419
  end
404
420
 
@@ -432,8 +448,12 @@ module Elasticsearch
432
448
  end
433
449
  end
434
450
 
435
- def warnings(warning)
436
- warn("warning: #{warning}")
451
+ def connection_headers(connection)
452
+ if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore
453
+ @request_options[:headers]
454
+ else
455
+ connection.connection.headers
456
+ end
437
457
  end
438
458
  end
439
459
  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.
@@ -63,6 +63,7 @@ module Elasticsearch
63
63
  include Base
64
64
 
65
65
  def initialize(arguments={}, &block)
66
+ @request_options = { headers: (arguments.dig(:transport_options, :headers) || {}) }
66
67
  @manticore = build_client(arguments[:options] || {})
67
68
  super(arguments, &block)
68
69
  end
@@ -82,7 +83,10 @@ module Elasticsearch
82
83
  #
83
84
  def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
84
85
  super do |connection, url|
85
- 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
86
90
  params[:headers] = headers if headers
87
91
  params = params.merge @request_options
88
92
  case method
@@ -109,7 +113,6 @@ module Elasticsearch
109
113
  # @return [Connections::Collection]
110
114
  #
111
115
  def __build_connections
112
- @request_options = {}
113
116
  apply_headers(@request_options, options[:transport_options])
114
117
  apply_headers(@request_options, options)
115
118
 
@@ -155,11 +158,11 @@ module Elasticsearch
155
158
  private
156
159
 
157
160
  def apply_headers(request_options, options)
158
- headers = (options && options[:headers]) || {}
161
+ headers = options&.[](:headers) || {}
159
162
  headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
160
163
  headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header
161
164
  headers[ACCEPT_ENCODING] = GZIP if use_compression?
162
- request_options.merge!(headers: headers)
165
+ request_options[:headers].merge!(headers)
163
166
  end
164
167
 
165
168
  def user_agent_header
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Elasticsearch
19
19
  module Transport
20
- VERSION = '7.13.3'.freeze
20
+ VERSION = '7.17.0'.freeze
21
21
  end
22
22
  end