elasticsearch-transport 7.4.0 → 7.17.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +30 -13
  3. data/Gemfile-faraday1.gemfile +47 -0
  4. data/README.md +159 -64
  5. data/Rakefile +63 -13
  6. data/elasticsearch-transport.gemspec +55 -63
  7. data/lib/elasticsearch/transport/client.rb +184 -59
  8. data/lib/elasticsearch/transport/meta_header.rb +135 -0
  9. data/lib/elasticsearch/transport/redacted.rb +16 -3
  10. data/lib/elasticsearch/transport/transport/base.rb +69 -30
  11. data/lib/elasticsearch/transport/transport/connections/collection.rb +18 -8
  12. data/lib/elasticsearch/transport/transport/connections/connection.rb +25 -9
  13. data/lib/elasticsearch/transport/transport/connections/selector.rb +16 -3
  14. data/lib/elasticsearch/transport/transport/errors.rb +17 -3
  15. data/lib/elasticsearch/transport/transport/http/curb.rb +60 -35
  16. data/lib/elasticsearch/transport/transport/http/faraday.rb +32 -9
  17. data/lib/elasticsearch/transport/transport/http/manticore.rb +57 -32
  18. data/lib/elasticsearch/transport/transport/loggable.rb +16 -3
  19. data/lib/elasticsearch/transport/transport/response.rb +17 -5
  20. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +16 -3
  21. data/lib/elasticsearch/transport/transport/sniffer.rb +35 -15
  22. data/lib/elasticsearch/transport/version.rb +17 -4
  23. data/lib/elasticsearch/transport.rb +35 -33
  24. data/lib/elasticsearch-transport.rb +16 -3
  25. data/spec/elasticsearch/connections/collection_spec.rb +28 -3
  26. data/spec/elasticsearch/connections/selector_spec.rb +16 -3
  27. data/spec/elasticsearch/transport/base_spec.rb +107 -49
  28. data/spec/elasticsearch/transport/client_spec.rb +734 -164
  29. data/spec/elasticsearch/transport/http/curb_spec.rb +126 -0
  30. data/spec/elasticsearch/transport/http/faraday_spec.rb +141 -0
  31. data/spec/elasticsearch/transport/http/manticore_spec.rb +161 -0
  32. data/spec/elasticsearch/transport/meta_header_spec.rb +301 -0
  33. data/spec/elasticsearch/transport/sniffer_spec.rb +16 -16
  34. data/spec/spec_helper.rb +32 -6
  35. data/test/integration/jruby_test.rb +43 -0
  36. data/test/integration/transport_test.rb +109 -46
  37. data/test/profile/client_benchmark_test.rb +16 -3
  38. data/test/test_helper.rb +26 -25
  39. data/test/unit/adapters_test.rb +88 -0
  40. data/test/unit/connection_test.rb +23 -5
  41. data/test/unit/response_test.rb +18 -5
  42. data/test/unit/serializer_test.rb +16 -3
  43. data/test/unit/transport_base_test.rb +33 -11
  44. data/test/unit/transport_curb_test.rb +16 -4
  45. data/test/unit/transport_faraday_test.rb +18 -5
  46. data/test/unit/transport_manticore_test.rb +258 -158
  47. metadata +64 -76
@@ -1,6 +1,19 @@
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
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
4
17
 
5
18
  # coding: utf-8
6
19
  lib = File.expand_path('../lib', __FILE__)
@@ -8,74 +21,53 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
8
21
  require 'elasticsearch/transport/version'
9
22
 
10
23
  Gem::Specification.new do |s|
11
- s.name = "elasticsearch-transport"
24
+ s.name = 'elasticsearch-transport'
12
25
  s.version = Elasticsearch::Transport::VERSION
13
- s.authors = ["Karel Minarik"]
14
- s.email = ["karel.minarik@elasticsearch.org"]
15
- s.summary = "Ruby client for Elasticsearch."
16
- s.homepage = "https://github.com/elasticsearch/elasticsearch-ruby/tree/master/elasticsearch-transport"
17
- s.license = "Apache-2.0"
18
-
26
+ s.authors = ['Karel Minarik']
27
+ s.email = ['karel.minarik@elasticsearch.org']
28
+ s.summary = 'Ruby client for Elasticsearch.'
29
+ s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/7.16/index.html'
30
+ s.license = 'Apache-2.0'
31
+ s.metadata = {
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
+ 'bug_tracker_uri' => 'https://github.com/elastic/elasticsearch-ruby/issues'
36
+ }
19
37
  s.files = `git ls-files`.split($/)
20
38
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
39
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
22
- s.require_paths = ["lib"]
23
-
24
- s.extra_rdoc_files = [ "README.md", "LICENSE" ]
25
- s.rdoc_options = [ "--charset=UTF-8" ]
26
-
27
- s.required_ruby_version = '>= 1.9'
28
-
29
- s.add_dependency "multi_json"
30
- s.add_dependency "faraday"
31
-
32
- if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
33
- s.add_dependency "system_timer"
34
- end
35
-
36
- s.add_development_dependency "bundler"
37
-
38
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
39
- s.add_development_dependency "rake", "~> 11.1"
40
- else
41
- s.add_development_dependency "rake", "< 11.0"
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 "yard"
48
- s.add_development_dependency "pry"
40
+ s.require_paths = ['lib']
49
41
 
50
- # Gems for testing integrations
51
- s.add_development_dependency "curb" unless defined? JRUBY_VERSION
52
- s.add_development_dependency "patron" unless defined? JRUBY_VERSION
53
- s.add_development_dependency "typhoeus", '~> 0.6'
54
- s.add_development_dependency "net-http-persistent"
55
- s.add_development_dependency "httpclient"
56
- s.add_development_dependency "manticore", '~> 0.6' if defined? JRUBY_VERSION
57
- s.add_development_dependency "hashie"
42
+ s.extra_rdoc_files = [ 'README.md', 'LICENSE' ]
43
+ s.rdoc_options = [ '--charset=UTF-8' ]
58
44
 
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
45
+ s.required_ruby_version = '>= 2.4'
64
46
 
65
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
66
- s.add_development_dependency "minitest"
67
- s.add_development_dependency "minitest-reporters"
68
- s.add_development_dependency "elasticsearch-extensions"
69
- s.add_development_dependency "ruby-prof" unless defined?(JRUBY_VERSION) || defined?(Rubinius)
70
- s.add_development_dependency "require-prof" unless defined?(JRUBY_VERSION) || defined?(Rubinius)
71
- s.add_development_dependency "simplecov"
72
- s.add_development_dependency "simplecov-rcov"
73
- s.add_development_dependency "cane"
74
- end
47
+ s.add_dependency 'multi_json'
48
+ s.add_dependency 'faraday', '>= 1', '< 3'
75
49
 
76
- if defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
77
- s.add_development_dependency "test-unit", '~> 2'
78
- end
50
+ # Faraday Adapters
51
+ s.add_development_dependency 'manticore' if defined? JRUBY_VERSION
52
+ s.add_development_dependency 'curb' unless defined? JRUBY_VERSION
53
+ s.add_development_dependency 'ansi'
54
+ s.add_development_dependency 'bundler'
55
+ s.add_development_dependency 'cane'
56
+ s.add_development_dependency 'elasticsearch', ['>= 7', '< 8.0.0']
57
+ s.add_development_dependency 'elasticsearch-extensions'
58
+ s.add_development_dependency 'hashie'
59
+ s.add_development_dependency 'minitest'
60
+ s.add_development_dependency 'minitest-reporters'
61
+ s.add_development_dependency 'mocha'
62
+ s.add_development_dependency 'patron' unless defined? JRUBY_VERSION
63
+ s.add_development_dependency 'pry'
64
+ s.add_development_dependency 'rake', '~> 13'
65
+ s.add_development_dependency 'require-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
66
+ s.add_development_dependency 'ruby-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius)
67
+ s.add_development_dependency 'shoulda-context'
68
+ s.add_development_dependency 'simplecov'
69
+ s.add_development_dependency 'test-unit', '~> 2'
70
+ s.add_development_dependency 'yard'
79
71
 
80
72
  s.description = <<-DESC.gsub(/^ /, '')
81
73
  Ruby client for Elasticsearch. See the `elasticsearch` gem for full integration.
@@ -1,18 +1,32 @@
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
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
4
17
 
5
18
  require 'base64'
19
+ require 'elasticsearch/transport/meta_header'
6
20
 
7
21
  module Elasticsearch
8
22
  module Transport
9
-
10
23
  # Handles communication with an Elasticsearch cluster.
11
24
  #
12
25
  # See {file:README.md README} for usage and code examples.
13
26
  #
14
27
  class Client
15
- DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday
28
+ include MetaHeader
29
+ DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday
16
30
 
17
31
  DEFAULT_LOGGER = lambda do
18
32
  require 'logger'
@@ -36,9 +50,15 @@ module Elasticsearch
36
50
  DEFAULT_HOST = 'localhost:9200'.freeze
37
51
 
38
52
  # The default port to use if connecting using a Cloud ID.
53
+ # Updated from 9243 to 443 in client version 7.10.1
39
54
  #
40
55
  # @since 7.2.0
41
- DEFAULT_CLOUD_PORT = 9243
56
+ DEFAULT_CLOUD_PORT = 443
57
+
58
+ # The default port to use if not otherwise specified.
59
+ #
60
+ # @since 7.2.0
61
+ DEFAULT_PORT = 9200
42
62
 
43
63
  # Returns the transport object.
44
64
  #
@@ -72,6 +92,8 @@ module Elasticsearch
72
92
  #
73
93
  # @option arguments [Boolean,Number] :retry_on_failure Retry X times when request fails before raising and
74
94
  # exception (false by default)
95
+ # @option arguments [Number] :delay_on_retry Delay in milliseconds between each retry (0 by default)
96
+ #
75
97
  # @option arguments Array<Number> :retry_on_status Retry when specific status codes are returned
76
98
  #
77
99
  # @option arguments [Boolean] :reload_on_failure Reload connections after failure (false by default)
@@ -99,6 +121,15 @@ module Elasticsearch
99
121
  # The default is false. Responses will automatically be inflated if they are compressed.
100
122
  # If a custom transport object is used, it must handle the request compression and response inflation.
101
123
  #
124
+ # @option api_key [String, Hash] :api_key Use API Key Authentication, either the base64 encoding of `id` and `api_key`
125
+ # joined by a colon as a String, or a hash with the `id` and `api_key` values.
126
+ # @option opaque_id_prefix [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client.
127
+ # This will be prepended to the id you set before each request
128
+ # if you're using X-Opaque-Id
129
+ # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
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
132
+ #
102
133
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
103
134
  #
104
135
  def initialize(arguments={}, &block)
@@ -108,61 +139,140 @@ module Elasticsearch
108
139
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
109
140
  @arguments[:reload_connections] ||= false
110
141
  @arguments[:retry_on_failure] ||= false
142
+ @arguments[:delay_on_retry] ||= 0
111
143
  @arguments[:reload_on_failure] ||= false
112
144
  @arguments[:randomize_hosts] ||= false
113
145
  @arguments[:transport_options] ||= {}
114
146
  @arguments[:http] ||= {}
115
- @options[:http] ||= {}
147
+ @arguments[:enable_meta_header] = arguments.fetch(:enable_meta_header) { true }
148
+ @options[:http] ||= {}
149
+
150
+ set_api_key if (@api_key = @arguments[:api_key])
151
+ set_compatibility_header if ENV['ELASTIC_CLIENT_APIVERSIONING']
116
152
 
117
153
  @seeds = extract_cloud_creds(@arguments)
118
154
  @seeds ||= __extract_hosts(@arguments[:hosts] ||
119
- @arguments[:host] ||
120
- @arguments[:url] ||
121
- @arguments[:urls] ||
122
- ENV['ELASTICSEARCH_URL'] ||
123
- DEFAULT_HOST)
155
+ @arguments[:host] ||
156
+ @arguments[:url] ||
157
+ @arguments[:urls] ||
158
+ ENV['ELASTICSEARCH_URL'] ||
159
+ DEFAULT_HOST)
124
160
 
125
161
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
162
+ @opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
163
+ @ca_fingerprint = @arguments.delete(:ca_fingerprint)
126
164
 
127
165
  if @arguments[:request_timeout]
128
- @arguments[:transport_options][:request] = { :timeout => @arguments[:request_timeout] }
166
+ @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
129
167
  end
130
168
 
131
169
  if @arguments[:transport]
132
170
  @transport = @arguments[:transport]
133
171
  else
134
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
135
- if transport_class == Transport::HTTP::Faraday
136
- @transport = transport_class.new(:hosts => @seeds, :options => @arguments) do |faraday|
137
- block.call faraday if block
138
- unless (h = faraday.builder.handlers.last) && h.name.start_with?("Faraday::Adapter")
139
- faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
140
- end
141
- end
142
- else
143
- @transport = transport_class.new(:hosts => @seeds, :options => @arguments)
144
- end
172
+ @transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
173
+ @transport = if @transport_class == Transport::HTTP::Faraday
174
+ @arguments[:adapter] ||= __auto_detect_adapter
175
+ set_meta_header # from include MetaHeader
176
+ @transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
177
+ faraday.adapter(@arguments[:adapter])
178
+ block&.call faraday
179
+ end
180
+ else
181
+ set_meta_header # from include MetaHeader
182
+ @transport_class.new(hosts: @seeds, options: @arguments)
183
+ end
145
184
  end
146
185
  end
147
186
 
148
187
  # Performs a request through delegation to {#transport}.
149
188
  #
150
- def perform_request(method, path, params={}, body=nil, headers=nil)
189
+ def perform_request(method, path, params = {}, body = nil, headers = nil)
151
190
  method = @send_get_body_as if 'GET' == method && body
191
+ if (opaque_id = params.delete(:opaque_id))
192
+ headers = {} if headers.nil?
193
+ opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
194
+ headers.merge!('X-Opaque-Id' => opaque_id)
195
+ end
196
+ validate_ca_fingerprints if @ca_fingerprint
152
197
  transport.perform_request(method, path, params, body, headers)
153
198
  end
154
199
 
155
200
  private
156
201
 
202
+ def set_api_key
203
+ @api_key = __encode(@api_key) if @api_key.is_a? Hash
204
+ add_header('Authorization' => "ApiKey #{@api_key}")
205
+ @arguments.delete(:user)
206
+ @arguments.delete(:password)
207
+ end
208
+
209
+ def set_compatibility_header
210
+ return unless ['1', 'true'].include?(ENV['ELASTIC_CLIENT_APIVERSIONING'])
211
+ return if instance_variable_get('@options').dig(:transport_options, :headers, 'Accept')
212
+
213
+ add_header(
214
+ {
215
+ 'Accept' => 'application/vnd.elasticsearch+json; compatible-with=7',
216
+ 'Content-Type' => 'application/vnd.elasticsearch+json; compatible-with=7'
217
+ }
218
+ )
219
+ end
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
+
246
+ def add_header(header)
247
+ headers = @arguments[:transport_options]&.[](:headers) || {}
248
+ headers.merge!(header)
249
+ @arguments[:transport_options].merge!(
250
+ headers: headers
251
+ )
252
+ end
253
+
157
254
  def extract_cloud_creds(arguments)
158
- return unless arguments[:cloud_id]
255
+ return unless arguments[:cloud_id] && !arguments[:cloud_id].empty?
256
+
159
257
  name = arguments[:cloud_id].split(':')[0]
160
258
  cloud_url, elasticsearch_instance = Base64.decode64(arguments[:cloud_id].gsub("#{name}:", '')).split('$')
161
- [ { scheme: 'https',
259
+
260
+ if cloud_url.include?(':')
261
+ url, port = cloud_url.split(':')
262
+ host = "#{elasticsearch_instance}.#{url}"
263
+ else
264
+ host = "#{elasticsearch_instance}.#{cloud_url}"
265
+ port = arguments[:port] || DEFAULT_CLOUD_PORT
266
+ end
267
+ [
268
+ {
269
+ scheme: 'https',
162
270
  user: arguments[:user],
163
271
  password: arguments[:password],
164
- host: "#{elasticsearch_instance}.#{cloud_url}",
165
- port: arguments[:port] || DEFAULT_CLOUD_PORT } ]
272
+ host: host,
273
+ port: port.to_i
274
+ }
275
+ ]
166
276
  end
167
277
 
168
278
  # Normalizes and returns hosts configuration.
@@ -195,39 +305,47 @@ module Elasticsearch
195
305
 
196
306
  def __parse_host(host)
197
307
  host_parts = case host
198
- when String
199
- if host =~ /^[a-z]+\:\/\//
200
- # Construct a new `URI::Generic` directly from the array returned by URI::split.
201
- # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
202
- uri = URI::Generic.new(*URI.split(host))
203
-
204
- { :scheme => uri.scheme,
205
- :user => uri.user,
206
- :password => uri.password,
207
- :host => uri.host,
208
- :path => uri.path,
209
- :port => uri.port }
210
- else
211
- host, port = host.split(':')
212
- { :host => host,
213
- :port => port }
214
- end
215
- when URI
216
- { :scheme => host.scheme,
217
- :user => host.user,
218
- :password => host.password,
219
- :host => host.host,
220
- :path => host.path,
221
- :port => host.port }
222
- when Hash
223
- host
308
+ when String
309
+ if host =~ /^[a-z]+\:\/\//
310
+ # Construct a new `URI::Generic` directly from the array returned by URI::split.
311
+ # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
312
+ uri = URI::Generic.new(*URI.split(host))
313
+ default_port = uri.scheme == 'https' ? 443 : DEFAULT_PORT
314
+ {
315
+ scheme: uri.scheme,
316
+ user: uri.user,
317
+ password: uri.password,
318
+ host: uri.host,
319
+ path: uri.path,
320
+ port: uri.port || default_port
321
+ }
322
+ else
323
+ host, port = host.split(':')
324
+ { host: host, port: port }
325
+ end
326
+ when URI
327
+ {
328
+ scheme: host.scheme,
329
+ user: host.user,
330
+ password: host.password,
331
+ host: host.host,
332
+ path: host.path,
333
+ port: host.port
334
+ }
335
+ when Hash
336
+ host
337
+ else
338
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
339
+ end
340
+ if @api_key
341
+ # Remove Basic Auth if using API KEY
342
+ host_parts.delete(:user)
343
+ host_parts.delete(:password)
224
344
  else
225
- raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
345
+ @options[:http][:user] ||= host_parts[:user]
346
+ @options[:http][:password] ||= host_parts[:password]
226
347
  end
227
348
 
228
- @options[:http][:user] ||= host_parts[:user]
229
- @options[:http][:password] ||= host_parts[:password]
230
-
231
349
  host_parts[:port] = host_parts[:port].to_i if host_parts[:port]
232
350
  host_parts[:path].chomp!('/') if host_parts[:path]
233
351
  host_parts
@@ -255,6 +373,13 @@ module Elasticsearch
255
373
  ::Faraday.default_adapter
256
374
  end
257
375
  end
376
+
377
+ # Encode credentials for the Authorization Header
378
+ # Credentials is the base64 encoding of id and api_key joined by a colon
379
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html
380
+ def __encode(api_key)
381
+ Base64.strict_encode64([api_key[:id], api_key[:api_key]].join(':'))
382
+ end
258
383
  end
259
384
  end
260
385
  end
@@ -0,0 +1,135 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'base64'
19
+
20
+ module Elasticsearch
21
+ module Transport
22
+ # Methods for the Elastic meta header used by Cloud.
23
+ # X-Elastic-Client-Meta HTTP header which is used by Elastic Cloud and can be disabled when
24
+ # instantiating the Client with the :enable_meta_header parameter set to `false`.
25
+ #
26
+ module MetaHeader
27
+ def set_meta_header
28
+ return if @arguments[:enable_meta_header] == false
29
+
30
+ service, version = meta_header_service_version
31
+
32
+ meta_headers = {
33
+ service.to_sym => version,
34
+ rb: RUBY_VERSION,
35
+ t: Elasticsearch::Transport::VERSION
36
+ }
37
+ meta_headers.merge!(meta_header_engine) if meta_header_engine
38
+ meta_headers.merge!(meta_header_adapter) if meta_header_adapter
39
+
40
+ add_header({ 'x-elastic-client-meta' => meta_headers.map { |k, v| "#{k}=#{v}" }.join(',') })
41
+ end
42
+
43
+ def meta_header_service_version
44
+ if enterprise_search?
45
+ Elastic::ENTERPRISE_SERVICE_VERSION
46
+ elsif elasticsearch?
47
+ Elastic::ELASTICSEARCH_SERVICE_VERSION
48
+ elsif defined?(Elasticsearch::VERSION)
49
+ [:es, client_meta_version(Elasticsearch::VERSION)]
50
+ else
51
+ [:es, client_meta_version(Elasticsearch::Transport::VERSION)]
52
+ end
53
+ end
54
+
55
+ def enterprise_search?
56
+ defined?(Elastic::ENTERPRISE_SERVICE_VERSION) &&
57
+ called_from?('enterprise-search-ruby')
58
+ end
59
+
60
+ def elasticsearch?
61
+ defined?(Elastic::ELASTICSEARCH_SERVICE_VERSION) &&
62
+ called_from?('elasticsearch')
63
+ end
64
+
65
+ def called_from?(service)
66
+ !caller.select { |c| c.match?(service) }.empty?
67
+ end
68
+
69
+ # We return the current version if it's a release, but if it's a pre/alpha/beta release we
70
+ # return <VERSION_NUMBER>p
71
+ #
72
+ def client_meta_version(version)
73
+ regexp = /^([0-9]+\.[0-9]+\.[0-9]+)(\.?[a-z0-9.-]+)?$/
74
+ match = version.match(regexp)
75
+ return "#{match[1]}p" if (match[2])
76
+
77
+ version
78
+ end
79
+
80
+ def meta_header_engine
81
+ case RUBY_ENGINE
82
+ when 'ruby'
83
+ {}
84
+ when 'jruby'
85
+ { jv: ENV_JAVA['java.version'], jr: JRUBY_VERSION }
86
+ when 'rbx'
87
+ { rbx: RUBY_VERSION }
88
+ else
89
+ { RUBY_ENGINE.to_sym => RUBY_VERSION }
90
+ end
91
+ end
92
+
93
+ # This function tries to define the version for the Faraday adapter. If it hasn't been loaded
94
+ # by the time we're calling this method, it's going to report the adapter (if we know it) but
95
+ # return 0 as the version. It won't report anything when using a custom adapter we don't
96
+ # identify.
97
+ #
98
+ # Returns a Hash<adapter_alias, version>
99
+ #
100
+ def meta_header_adapter
101
+ if @transport_class == Transport::HTTP::Faraday
102
+ version = '0'
103
+ adapter_version = case @arguments[:adapter]
104
+ when :patron
105
+ version = Patron::VERSION if defined?(::Patron::VERSION)
106
+ {pt: version}
107
+ when :net_http
108
+ version = if defined?(Net::HTTP::VERSION)
109
+ Net::HTTP::VERSION
110
+ elsif defined?(Net::HTTP::HTTPVersion)
111
+ Net::HTTP::HTTPVersion
112
+ end
113
+ {nh: version}
114
+ when :typhoeus
115
+ version = Typhoeus::VERSION if defined?(::Typhoeus::VERSION)
116
+ {ty: version}
117
+ when :httpclient
118
+ version = HTTPClient::VERSION if defined?(HTTPClient::VERSION)
119
+ {hc: version}
120
+ when :net_http_persistent
121
+ version = Net::HTTP::Persistent::VERSION if defined?(Net::HTTP::Persistent::VERSION)
122
+ {np: version}
123
+ else
124
+ {}
125
+ end
126
+ {fd: Faraday::VERSION}.merge(adapter_version)
127
+ elsif defined?(Transport::HTTP::Curb) && @transport_class == Transport::HTTP::Curb
128
+ {cl: Curl::CURB_VERSION}
129
+ elsif defined?(Transport::HTTP::Manticore) && @transport_class == Transport::HTTP::Manticore
130
+ {mc: Manticore::VERSION}
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -1,6 +1,19 @@
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
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
4
17
 
5
18
  module Elasticsearch
6
19
  module Transport