elasticsearch-transport 6.3.0 → 6.8.3

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +86 -32
  3. data/elasticsearch-transport.gemspec +45 -64
  4. data/lib/elasticsearch-transport.rb +4 -0
  5. data/lib/elasticsearch/transport.rb +4 -0
  6. data/lib/elasticsearch/transport/client.rb +141 -20
  7. data/lib/elasticsearch/transport/redacted.rb +4 -0
  8. data/lib/elasticsearch/transport/transport/base.rb +17 -7
  9. data/lib/elasticsearch/transport/transport/connections/collection.rb +4 -0
  10. data/lib/elasticsearch/transport/transport/connections/connection.rb +4 -0
  11. data/lib/elasticsearch/transport/transport/connections/selector.rb +4 -0
  12. data/lib/elasticsearch/transport/transport/errors.rb +4 -0
  13. data/lib/elasticsearch/transport/transport/http/curb.rb +6 -2
  14. data/lib/elasticsearch/transport/transport/http/faraday.rb +6 -2
  15. data/lib/elasticsearch/transport/transport/http/manticore.rb +5 -1
  16. data/lib/elasticsearch/transport/transport/response.rb +4 -0
  17. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +4 -0
  18. data/lib/elasticsearch/transport/transport/sniffer.rb +31 -3
  19. data/lib/elasticsearch/transport/version.rb +5 -1
  20. data/spec/elasticsearch/transport/base_spec.rb +187 -8
  21. data/spec/elasticsearch/transport/client_spec.rb +157 -23
  22. data/spec/elasticsearch/transport/meta_header_spec.rb +214 -0
  23. data/spec/elasticsearch/transport/sniffer_spec.rb +269 -0
  24. data/spec/spec_helper.rb +11 -0
  25. data/test/integration/transport_test.rb +4 -0
  26. data/test/profile/client_benchmark_test.rb +4 -0
  27. data/test/test_helper.rb +4 -0
  28. data/test/unit/connection_collection_test.rb +4 -0
  29. data/test/unit/connection_selector_test.rb +4 -0
  30. data/test/unit/connection_test.rb +4 -0
  31. data/test/unit/response_test.rb +5 -1
  32. data/test/unit/serializer_test.rb +4 -0
  33. data/test/unit/transport_base_test.rb +4 -0
  34. data/test/unit/transport_curb_test.rb +4 -0
  35. data/test/unit/transport_faraday_test.rb +4 -0
  36. data/test/unit/transport_manticore_test.rb +4 -0
  37. metadata +80 -54
  38. data/test/unit/sniffer_test.rb +0 -179
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ac9d2e54aee696db7620c3e1b8d9144c28adc52abb6807c35eea61bb01103cb
4
- data.tar.gz: 801ee3f07fdd965739ef4d28efe3b658e21d1f3bad2c63aca323f8dc877327bd
3
+ metadata.gz: 84ce4d26d5dd03b5bc17d89e4ce0a873b5d57083f07cf54cf78552d7e88ff61f
4
+ data.tar.gz: 69b164ec36cd3cb2ee3355c0806a0109e21a69ca605a9f87b020f9d8476e5096
5
5
  SHA512:
6
- metadata.gz: 33162c0aafa4702e15a01e86fd5b34962cd28af4146d97f3e69de15b1271cd2ea4021facb12928fd69440fd64a31fef4aaf306a78327da2336958f55a3c36313
7
- data.tar.gz: dd0440bd172c86867ada057bd5d1f53b765714b2eb594dee643da5beec9a74da6a0447a07ed813aeb83f21cdcd823429a6f987c610553554f9acd1654e4f077d
6
+ metadata.gz: e6a9fb8c521ffedd0faf79006e477e4360a2af6df876cee41ddd87278ddf06de34a69e9ccbdad0d83629a0cf1c52d0eefbf4facdb38a591a219c156a5d7d3fdf
7
+ data.tar.gz: 2d729907c495a2946b68a193262b14261534d6811b2ea5517248977758ed869c3c2803284ff69bc9b9f5a863a9440a9f675f53287f136e5813677b95b1d106c8
data/README.md CHANGED
@@ -28,12 +28,16 @@ Features overview:
28
28
  * Node reloading (based on cluster state) on errors or on demand
29
29
 
30
30
  For optimal performance, use a HTTP library which supports persistent ("keep-alive") connections,
31
- such as [Typhoeus](https://github.com/typhoeus/typhoeus).
32
- Just require the library (`require 'typhoeus'; require 'typhoeus/adapters/faraday'`) in your code,
33
- and it will be automatically used; currently these libraries will be automatically detected and used:
34
- [Patron](https://github.com/toland/patron),
35
- [HTTPClient](https://rubygems.org/gems/httpclient) and
36
- [Net::HTTP::Persistent](https://rubygems.org/gems/net-http-persistent).
31
+ such as [patron](https://github.com/toland/patron) or [Typhoeus](https://github.com/typhoeus/typhoeus).
32
+ Just require the library (`require 'patron'`) in your code, and it will be automatically used.
33
+
34
+ Currently these libraries will be automatically detected and used:
35
+ - [Patron](https://github.com/toland/patron)
36
+ - [Typhoeus](https://github.com/typhoeus/typhoeus)
37
+ - [HTTPClient](https://rubygems.org/gems/httpclient)
38
+ - [Net::HTTP::Persistent](https://rubygems.org/gems/net-http-persistent)
39
+
40
+ **Note on [Typhoeus](https://github.com/typhoeus/typhoeus)**: You need to use v1.4.0 or up since older versions are not compatible with Faraday 1.0.
37
41
 
38
42
  For detailed information, see example configurations [below](#transport-implementations).
39
43
 
@@ -69,6 +73,22 @@ Full documentation is available at <http://rubydoc.info/gems/elasticsearch-trans
69
73
 
70
74
  ## Configuration
71
75
 
76
+ * [Setting Hosts](#setting-hosts)
77
+ * [Default port](#default-port)
78
+ * [Connect using an Elastic Cloud ID](#connect-using-an-elastic-cloud-id)
79
+ * [Authentication](#authentication)
80
+ * [Logging](#logging)
81
+ * [Identifying running tasks with X-Opaque-Id](#identifying-running-tasks-with-x-opaque-id)
82
+ * [Setting Timeouts](#setting-timeouts)
83
+ * [Randomizing Hosts](#randomizing-hosts)
84
+ * [Retrying on Failures](#retrying-on-failures)
85
+ * [Reloading Hosts](#reloading-hosts)
86
+ * [Connection Selector](#connection-selector)
87
+ * [Transport Implementations](#transport-implementations)
88
+ * [Serializer implementations](#serializer-implementations)
89
+ * [Exception Handling](#exception-handling)
90
+ * [Development and Community](#development-and-community)
91
+
72
92
  The client supports many configurations options for setting up and managing connections,
73
93
  configuring logging, customizing the transport library, etc.
74
94
 
@@ -128,6 +148,26 @@ use the `transport_options` option:
128
148
  Elasticsearch::Client.new url: 'https://username:password@example.com:9200',
129
149
  transport_options: { ssl: { ca_file: '/path/to/cacert.pem' } }
130
150
 
151
+ You can also use [**API Key authentication**](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html):
152
+
153
+ ``` ruby
154
+ Elasticsearch::Client.new(
155
+ host: host,
156
+ transport_options: transport_options,
157
+ api_key: credentials
158
+ )
159
+ ```
160
+
161
+ Where credentials is either the base64 encoding of `id` and `api_key` joined by a colon or a hash with the `id` and `api_key`:
162
+
163
+ ``` ruby
164
+ Elasticsearch::Client.new(
165
+ host: host,
166
+ transport_options: transport_options,
167
+ api_key: {id: 'my_id', api_key: 'my_api_key'}
168
+ )
169
+ ```
170
+
131
171
  ### Logging
132
172
 
133
173
  To log requests and responses to standard output with the default logger (an instance of Ruby's {::Logger} class),
@@ -158,6 +198,29 @@ You can pass the client any conforming logger implementation:
158
198
 
159
199
  client = Elasticsearch::Client.new logger: log
160
200
 
201
+ ### Identifying running tasks with X-Opaque-Id
202
+
203
+ The X-Opaque-Id header allows to track certain calls, or associate certain tasks with the client that started them ([more on the Elasticsearch docs](https://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html#_identifying_running_tasks)). To use this feature, you need to set an id for `opaque_id` on the client on each request. Example:
204
+
205
+ ```ruby
206
+ client = Elasticsearch::Client.new
207
+ client.search(index: 'myindex', q: 'title:test', opaque_id: '123456')
208
+ ```
209
+ The search request will include the following HTTP Header:
210
+ ```
211
+ X-Opaque-Id: 123456
212
+ ```
213
+
214
+ You can also set a prefix for X-Opaque-Id when initializing the client. This will be prepended to the id you set before each request if you're using X-Opaque-Id. Example:
215
+ ```ruby
216
+ client = Elasticsearch::Client.new(opaque_id_prefix: 'eu-west1')
217
+ client.search(index: 'myindex', q: 'title:test', opaque_id: '123456')
218
+ ```
219
+ The request will include the following HTTP Header:
220
+ ```
221
+ X-Opaque-Id: eu-west1_123456
222
+ ```
223
+
161
224
  ### Setting Timeouts
162
225
 
163
226
  For many operations in Elasticsearch, the default timeouts of HTTP libraries are too low.
@@ -292,44 +355,35 @@ constructor, use the `transport_options` key:
292
355
 
293
356
  To configure the _Faraday_ instance directly, use a block:
294
357
 
295
- require 'typhoeus'
296
- require 'typhoeus/adapters/faraday'
358
+ require 'patron'
297
359
 
298
360
  client = Elasticsearch::Client.new(host: 'localhost', port: '9200') do |f|
299
361
  f.response :logger
300
- f.adapter :typhoeus
362
+ f.adapter :patron
301
363
  end
302
364
 
303
- You can use any standard Faraday middleware and plugins in the configuration block,
304
- for example sign the requests for the [AWS Elasticsearch service](https://aws.amazon.com/elasticsearch-service/):
305
-
306
- require 'faraday_middleware/aws_signers_v4'
307
-
308
- client = Elasticsearch::Client.new url: 'https://search-my-cluster-abc123....es.amazonaws.com' do |f|
309
- f.request :aws_signers_v4,
310
- credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY'], ENV['AWS_SECRET_ACCESS_KEY']),
311
- service_name: 'es',
312
- region: 'us-east-1'
313
- end
365
+ 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.
314
366
 
315
367
  You can also initialize the transport class yourself, and pass it to the client constructor
316
368
  as the `transport` argument:
317
369
 
318
- require 'typhoeus'
319
- require 'typhoeus/adapters/faraday'
370
+ ```ruby
371
+ require 'patron'
320
372
 
321
- transport_configuration = lambda do |f|
322
- f.response :logger
323
- f.adapter :typhoeus
324
- end
373
+ transport_configuration = lambda do |f|
374
+ f.response :logger
375
+ f.adapter :patron
376
+ end
325
377
 
326
- transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \
327
- hosts: [ { host: 'localhost', port: '9200' } ],
328
- &transport_configuration
378
+ transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \
379
+ hosts: [ { host: 'localhost', port: '9200' } ],
380
+ &transport_configuration
381
+
382
+ # Pass the transport to the client
383
+ #
384
+ client = Elasticsearch::Client.new transport: transport
385
+ ```
329
386
 
330
- # Pass the transport to the client
331
- #
332
- client = Elasticsearch::Client.new transport: transport
333
387
 
334
388
  Instead of passing the transport to the constructor, you can inject it at run time:
335
389
 
@@ -4,75 +4,56 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'elasticsearch/transport/version'
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = "elasticsearch-transport"
7
+ s.name = 'elasticsearch-transport'
8
8
  s.version = Elasticsearch::Transport::VERSION
9
- s.authors = ["Karel Minarik"]
10
- s.email = ["karel.minarik@elasticsearch.org"]
11
- s.summary = "Ruby client for Elasticsearch."
12
- s.homepage = "https://github.com/elasticsearch/elasticsearch-ruby/tree/master/elasticsearch-transport"
13
- s.license = "Apache-2.0"
14
-
9
+ s.authors = ['Karel Minarik']
10
+ s.email = ['karel.minarik@elasticsearch.org']
11
+ s.summary = 'Ruby client for Elasticsearch.'
12
+ s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html'
13
+ s.license = 'Apache-2.0'
14
+ s.metadata = {
15
+ 'homepage_uri' => 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html',
16
+ 'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/6.x/CHANGELOG.md',
17
+ 'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/6.x/elasticsearch-transport',
18
+ 'bug_tracker_uri' => 'https://github.com/elastic/elasticsearch-ruby/issues'
19
+ }
15
20
  s.files = `git ls-files`.split($/)
16
21
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
22
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
- s.require_paths = ["lib"]
19
-
20
- s.extra_rdoc_files = [ "README.md", "LICENSE.txt" ]
21
- s.rdoc_options = [ "--charset=UTF-8" ]
22
-
23
- s.required_ruby_version = '>= 1.9'
24
-
25
- s.add_dependency "multi_json"
26
- s.add_dependency "faraday"
27
-
28
- if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
29
- s.add_dependency "system_timer"
30
- end
31
-
32
- s.add_development_dependency "bundler"
33
-
34
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
35
- s.add_development_dependency "rake", "~> 11.1"
36
- else
37
- s.add_development_dependency "rake", "< 11.0"
38
- end
39
-
40
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
41
- s.add_development_dependency "elasticsearch-extensions"
42
- end
43
-
44
- s.add_development_dependency "ansi"
45
- s.add_development_dependency "shoulda-context"
46
- s.add_development_dependency "mocha"
47
- s.add_development_dependency "turn"
48
- s.add_development_dependency "yard"
49
- s.add_development_dependency "pry"
50
-
23
+ s.require_paths = ['lib']
24
+
25
+ s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
26
+ s.rdoc_options = ['--charset=UTF-8']
27
+
28
+ s.required_ruby_version = '>= 2.4'
29
+
30
+ s.add_dependency 'multi_json'
31
+ s.add_dependency 'faraday', '~> 1'
32
+ s.add_development_dependency 'ansi'
33
+ s.add_development_dependency 'bundler'
34
+ s.add_development_dependency 'elasticsearch-extensions'
35
+ s.add_development_dependency 'mocha'
36
+ s.add_development_dependency 'pry'
37
+ s.add_development_dependency 'rake', '~> 13'
38
+ s.add_development_dependency 'shoulda-context'
39
+ s.add_development_dependency 'turn'
40
+ s.add_development_dependency 'yard'
51
41
  # Gems for testing integrations
52
- s.add_development_dependency "curb" unless defined? JRUBY_VERSION
53
- s.add_development_dependency "patron" unless defined? JRUBY_VERSION
54
- s.add_development_dependency "typhoeus", '~> 0.6'
55
- s.add_development_dependency "net-http-persistent"
56
- s.add_development_dependency "manticore", '~> 0.5.2' if defined? JRUBY_VERSION
57
- s.add_development_dependency "hashie"
58
-
59
- # Prevent unit test failures on Ruby 1.8
60
- if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
61
- s.add_development_dependency "test-unit", '~> 2'
62
- s.add_development_dependency "json", '~> 1.8'
63
- end
64
-
65
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
66
- s.add_development_dependency "minitest", "~> 4.0"
67
- s.add_development_dependency "ruby-prof" unless defined?(JRUBY_VERSION) || defined?(Rubinius)
68
- s.add_development_dependency "require-prof" unless defined?(JRUBY_VERSION) || defined?(Rubinius)
69
- s.add_development_dependency "simplecov"
70
- s.add_development_dependency "simplecov-rcov"
71
- s.add_development_dependency "cane"
72
- end
73
-
74
- if defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
75
- s.add_development_dependency "test-unit", '~> 2'
42
+ s.add_development_dependency 'cane'
43
+ s.add_development_dependency 'hashie'
44
+ s.add_development_dependency 'httpclient'
45
+ s.add_development_dependency 'manticore', '~> 0.5.2' if defined? JRUBY_VERSION
46
+ s.add_development_dependency 'minitest', '~> 4.0'
47
+ s.add_development_dependency 'net-http-persistent'
48
+ s.add_development_dependency 'simplecov', '~> 0.17', '< 0.18'
49
+ s.add_development_dependency 'simplecov-rcov'
50
+ s.add_development_dependency 'test-unit', '~> 2'
51
+ s.add_development_dependency 'typhoeus', '~> 1.4'
52
+ unless defined?(JRUBY_VERSION) || defined?(Rubinius)
53
+ s.add_development_dependency 'curb'
54
+ s.add_development_dependency 'patron'
55
+ s.add_development_dependency 'require-prof'
56
+ s.add_development_dependency 'ruby-prof'
76
57
  end
77
58
 
78
59
  s.description = <<-DESC.gsub(/^ /, '')
@@ -1 +1,5 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+
1
5
  require 'elasticsearch/transport'
@@ -1,3 +1,7 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+
1
5
  require "uri"
2
6
  require "time"
3
7
  require "timeout"
@@ -1,3 +1,7 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+
1
5
  module Elasticsearch
2
6
  module Transport
3
7
 
@@ -85,11 +89,19 @@ module Elasticsearch
85
89
  # @option arguments [String] :send_get_body_as Specify the HTTP method to use for GET requests with a body.
86
90
  # (Default: GET)
87
91
  #
92
+ # @option api_key [String, Hash] :api_key Use API Key Authentication, either the base64 encoding of `id` and `api_key`
93
+ # joined by a colon as a String, or a hash with the `id` and `api_key` values.
94
+ # @option opaque_id_prefix [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client. This
95
+ # will be prepended to the id you set before each request if
96
+ # you're using X-Opaque-Id
97
+ # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
98
+ # (Default: true)
99
+ #
88
100
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
89
101
  #
90
- def initialize(arguments={}, &block)
91
- @options = arguments
92
- @arguments = arguments
102
+ def initialize(arguments = {}, &block)
103
+ @options = arguments.each_with_object({}){ |(k,v), args| args[k.to_sym] = v }
104
+ @arguments = @options
93
105
  @arguments[:logger] ||= @arguments[:log] ? DEFAULT_LOGGER.call() : nil
94
106
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
95
107
  @arguments[:reload_connections] ||= false
@@ -98,9 +110,13 @@ module Elasticsearch
98
110
  @arguments[:randomize_hosts] ||= false
99
111
  @arguments[:transport_options] ||= {}
100
112
  @arguments[:http] ||= {}
101
- @options[:http] ||= {}
113
+ @arguments[:enable_meta_header] = arguments.fetch(:enable_meta_header) { true }
114
+ @options[:http] ||= {}
115
+
116
+ set_api_key if (@api_key = @arguments[:api_key])
102
117
 
103
- @seeds = __extract_hosts(@arguments[:hosts] ||
118
+
119
+ @seeds ||= __extract_hosts(@arguments[:hosts] ||
104
120
  @arguments[:host] ||
105
121
  @arguments[:url] ||
106
122
  @arguments[:urls] ||
@@ -108,9 +124,10 @@ module Elasticsearch
108
124
  DEFAULT_HOST)
109
125
 
110
126
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
127
+ @opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
111
128
 
112
129
  if @arguments[:request_timeout]
113
- @arguments[:transport_options][:request] = { :timeout => @arguments[:request_timeout] }
130
+ @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
114
131
  end
115
132
 
116
133
  @arguments[:transport_options][:headers] ||= {}
@@ -122,29 +139,120 @@ module Elasticsearch
122
139
  if @arguments[:transport]
123
140
  @transport = @arguments[:transport]
124
141
  else
125
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
126
- if transport_class == Transport::HTTP::Faraday
127
- @transport = transport_class.new(:hosts => @seeds, :options => @arguments) do |faraday|
128
- block.call faraday if block
129
- unless (h = faraday.builder.handlers.last) && h.name.start_with?("Faraday::Adapter")
130
- faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
131
- end
132
- end
133
- else
134
- @transport = transport_class.new(:hosts => @seeds, :options => @arguments)
135
- end
142
+ @transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
143
+ @transport = if @transport_class == Transport::HTTP::Faraday
144
+ @arguments[:adapter] ||= __auto_detect_adapter
145
+ set_meta_header
146
+ @transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
147
+ faraday.adapter(@arguments[:adapter])
148
+ block&.call faraday
149
+ end
150
+ else
151
+ set_meta_header
152
+ @transport_class.new(hosts: @seeds, options: @arguments)
153
+ end
136
154
  end
137
155
  end
138
156
 
139
157
  # Performs a request through delegation to {#transport}.
140
158
  #
141
- def perform_request(method, path, params={}, body=nil, headers=nil)
159
+ def perform_request(method, path, params = {}, body = nil, headers = nil)
142
160
  method = @send_get_body_as if 'GET' == method && body
161
+ if (opaque_id = params.delete(:opaque_id))
162
+ headers = {} if headers.nil?
163
+ opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
164
+ headers.merge!('X-Opaque-Id' => opaque_id)
165
+ end
143
166
  transport.perform_request(method, path, params, body, headers)
144
167
  end
145
168
 
146
169
  private
147
170
 
171
+ def set_api_key
172
+ @api_key = __encode(@api_key) if @api_key.is_a? Hash
173
+ add_header('Authorization' => "ApiKey #{@api_key}")
174
+ @arguments.delete(:user)
175
+ @arguments.delete(:password)
176
+ end
177
+
178
+ def add_header(header)
179
+ headers = @arguments[:transport_options]&.[](:headers) || {}
180
+ headers.merge!(header)
181
+ @arguments[:transport_options].merge!(
182
+ headers: headers
183
+ )
184
+ end
185
+
186
+ def set_meta_header
187
+ return if @arguments[:enable_meta_header] == false
188
+
189
+ service, version = meta_header_service_version
190
+
191
+ meta_headers = {
192
+ service.to_sym => version,
193
+ rb: RUBY_VERSION,
194
+ t: Elasticsearch::Transport::VERSION
195
+ }
196
+ meta_headers.merge!(meta_header_engine) if meta_header_engine
197
+ meta_headers.merge!(meta_header_adapter) if meta_header_adapter
198
+
199
+ add_header({ 'x-elastic-client-meta' => meta_headers.map { |k, v| "#{k}=#{v}" }.join(',') })
200
+ end
201
+
202
+ def meta_header_service_version
203
+ if defined?(Elastic::META_HEADER_SERVICE_VERSION)
204
+ Elastic::META_HEADER_SERVICE_VERSION
205
+ elsif defined?(Elasticsearch::VERSION)
206
+ [:es, client_meta_version(Elasticsearch::VERSION)]
207
+ else
208
+ [:es, client_meta_version(Elasticsearch::Transport::VERSION)]
209
+ end
210
+ end
211
+
212
+ def client_meta_version(version)
213
+ regexp = /^([0-9]+\.[0-9]+\.[0-9]+)(\.?[a-z0-9.-]+)?$/
214
+ match = version.match(regexp)
215
+ return "#{match[1]}p" if (match[2])
216
+
217
+ version
218
+ end
219
+
220
+ def meta_header_engine
221
+ case RUBY_ENGINE
222
+ when 'ruby'
223
+ {}
224
+ when 'jruby'
225
+ { jv: ENV_JAVA['java.version'], jr: JRUBY_VERSION }
226
+ when 'rbx'
227
+ { rbx: RUBY_VERSION }
228
+ else
229
+ { RUBY_ENGINE.to_sym => RUBY_VERSION }
230
+ end
231
+ end
232
+
233
+ def meta_header_adapter
234
+ if @transport_class == Transport::HTTP::Faraday
235
+ {fd: Faraday::VERSION}.merge(
236
+ case @arguments[:adapter]
237
+ when :patron
238
+ {pt: Patron::VERSION}
239
+ when :net_http
240
+ {nh: defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}
241
+ when :typhoeus
242
+ {ty: Typhoeus::VERSION}
243
+ when :httpclient
244
+ {hc: HTTPClient::VERSION}
245
+ when :net_http_persistent
246
+ {np: Net::HTTP::Persistent::VERSION}
247
+ end
248
+ )
249
+ elsif defined?(Transport::HTTP::Curb) && @transport_class == Transport::HTTP::Curb
250
+ {cl: Curl::CURB_VERSION}
251
+ elsif defined?(Transport::HTTP::Manticore) && @transport_class == Transport::HTTP::Manticore
252
+ {mc: Manticore::VERSION}
253
+ end
254
+ end
255
+
148
256
  # Normalizes and returns hosts configuration.
149
257
  #
150
258
  # Arrayifies the `hosts_config` argument and extracts `host` and `port` info from strings.
@@ -202,8 +310,14 @@ module Elasticsearch
202
310
  raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
203
311
  end
204
312
 
205
- @options[:http][:user] ||= host_parts[:user]
206
- @options[:http][:password] ||= host_parts[:password]
313
+ if @api_key
314
+ # Remove Basic Auth if using API KEY
315
+ host_parts.delete(:user)
316
+ host_parts.delete(:password)
317
+ else
318
+ @options[:http][:user] ||= host_parts[:user]
319
+ @options[:http][:password] ||= host_parts[:password]
320
+ end
207
321
 
208
322
  host_parts[:port] = host_parts[:port].to_i if host_parts[:port]
209
323
  host_parts[:path].chomp!('/') if host_parts[:path]
@@ -232,6 +346,13 @@ module Elasticsearch
232
346
  ::Faraday.default_adapter
233
347
  end
234
348
  end
349
+
350
+ # Encode credentials for the Authorization Header
351
+ # Credentials is the base64 encoding of id and api_key joined by a colon
352
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html
353
+ def __encode(api_key)
354
+ Base64.strict_encode64([api_key[:id], api_key[:api_key]].join(':'))
355
+ end
235
356
  end
236
357
  end
237
358
  end