elasticsearch-transport 5.0.5 → 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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +5 -0
  3. data/README.md +88 -34
  4. data/Rakefile +14 -17
  5. data/elasticsearch-transport.gemspec +45 -63
  6. data/lib/elasticsearch/transport/client.rb +207 -67
  7. data/lib/elasticsearch/transport/redacted.rb +79 -0
  8. data/lib/elasticsearch/transport/transport/base.rb +28 -14
  9. data/lib/elasticsearch/transport/transport/connections/collection.rb +4 -0
  10. data/lib/elasticsearch/transport/transport/connections/connection.rb +5 -1
  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 +7 -2
  14. data/lib/elasticsearch/transport/transport/http/faraday.rb +11 -8
  15. data/lib/elasticsearch/transport/transport/http/manticore.rb +6 -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/lib/elasticsearch/transport.rb +5 -0
  21. data/lib/elasticsearch-transport.rb +4 -0
  22. data/spec/elasticsearch/transport/base_spec.rb +260 -0
  23. data/spec/elasticsearch/transport/client_spec.rb +1063 -0
  24. data/spec/elasticsearch/transport/meta_header_spec.rb +214 -0
  25. data/spec/elasticsearch/transport/sniffer_spec.rb +269 -0
  26. data/spec/spec_helper.rb +72 -0
  27. data/test/integration/transport_test.rb +9 -5
  28. data/test/profile/client_benchmark_test.rb +23 -25
  29. data/test/test_helper.rb +10 -0
  30. data/test/unit/connection_collection_test.rb +4 -0
  31. data/test/unit/connection_selector_test.rb +4 -0
  32. data/test/unit/connection_test.rb +4 -0
  33. data/test/unit/response_test.rb +5 -1
  34. data/test/unit/serializer_test.rb +4 -0
  35. data/test/unit/transport_base_test.rb +21 -1
  36. data/test/unit/transport_curb_test.rb +12 -0
  37. data/test/unit/transport_faraday_test.rb +16 -0
  38. data/test/unit/transport_manticore_test.rb +11 -0
  39. metadata +90 -76
  40. data/test/integration/client_test.rb +0 -237
  41. data/test/unit/client_test.rb +0 -366
  42. data/test/unit/sniffer_test.rb +0 -179
@@ -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
 
@@ -24,6 +28,11 @@ module Elasticsearch
24
28
  logger
25
29
  end
26
30
 
31
+ # The default host and port to use if not otherwise specified.
32
+ #
33
+ # @since 7.0.0
34
+ DEFAULT_HOST = 'localhost:9200'.freeze
35
+
27
36
  # Returns the transport object.
28
37
  #
29
38
  # @see Elasticsearch::Transport::Transport::Base
@@ -80,17 +89,19 @@ module Elasticsearch
80
89
  # @option arguments [String] :send_get_body_as Specify the HTTP method to use for GET requests with a body.
81
90
  # (Default: GET)
82
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
+ #
83
100
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
84
101
  #
85
- def initialize(arguments={}, &block)
86
- @arguments = arguments
87
-
88
- hosts = @arguments[:hosts] || \
89
- @arguments[:host] || \
90
- @arguments[:url] || \
91
- @arguments[:urls] || \
92
- ENV.fetch('ELASTICSEARCH_URL', 'localhost:9200')
93
-
102
+ def initialize(arguments = {}, &block)
103
+ @options = arguments.each_with_object({}){ |(k,v), args| args[k.to_sym] = v }
104
+ @arguments = @options
94
105
  @arguments[:logger] ||= @arguments[:log] ? DEFAULT_LOGGER.call() : nil
95
106
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
96
107
  @arguments[:reload_connections] ||= false
@@ -99,36 +110,147 @@ module Elasticsearch
99
110
  @arguments[:randomize_hosts] ||= false
100
111
  @arguments[:transport_options] ||= {}
101
112
  @arguments[:http] ||= {}
113
+ @arguments[:enable_meta_header] = arguments.fetch(:enable_meta_header) { true }
114
+ @options[:http] ||= {}
102
115
 
103
- @arguments[:transport_options].update(:request => { :timeout => @arguments[:request_timeout] } ) if @arguments[:request_timeout]
116
+ set_api_key if (@api_key = @arguments[:api_key])
104
117
 
105
- @arguments[:transport_options][:headers] ||= {}
106
- @arguments[:transport_options][:headers].update 'Content-Type' => 'application/json' unless @arguments[:transport_options][:headers].keys.any? {|k| k.to_s.downcase =~ /content\-?\_?type/}
118
+
119
+ @seeds ||= __extract_hosts(@arguments[:hosts] ||
120
+ @arguments[:host] ||
121
+ @arguments[:url] ||
122
+ @arguments[:urls] ||
123
+ ENV['ELASTICSEARCH_URL'] ||
124
+ DEFAULT_HOST)
107
125
 
108
126
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
127
+ @opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
109
128
 
110
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
129
+ if @arguments[:request_timeout]
130
+ @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
131
+ end
111
132
 
112
- @transport = @arguments[:transport] || begin
113
- if transport_class == Transport::HTTP::Faraday
114
- transport_class.new(:hosts => __extract_hosts(hosts, @arguments), :options => @arguments) do |faraday|
115
- block.call faraday if block
116
- unless (h = faraday.builder.handlers.last) && h.name.start_with?("Faraday::Adapter")
117
- faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
118
- end
119
- end
120
- else
121
- transport_class.new(:hosts => __extract_hosts(hosts, @arguments), :options => @arguments)
122
- end
133
+ @arguments[:transport_options][:headers] ||= {}
134
+
135
+ unless @arguments[:transport_options][:headers].keys.any? {|k| k.to_s.downcase =~ /content\-?\_?type/}
136
+ @arguments[:transport_options][:headers]['Content-Type'] = 'application/json'
137
+ end
138
+
139
+ if @arguments[:transport]
140
+ @transport = @arguments[:transport]
141
+ else
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
123
154
  end
124
155
  end
125
156
 
126
157
  # Performs a request through delegation to {#transport}.
127
158
  #
128
- def perform_request(method, path, params={}, body=nil)
159
+ def perform_request(method, path, params = {}, body = nil, headers = nil)
129
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
166
+ transport.perform_request(method, path, params, body, headers)
167
+ end
168
+
169
+ private
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
130
198
 
131
- transport.perform_request method, path, params, body
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
132
254
  end
133
255
 
134
256
  # Normalizes and returns hosts configuration.
@@ -143,52 +265,63 @@ module Elasticsearch
143
265
  #
144
266
  # @api private
145
267
  #
146
- def __extract_hosts(hosts_config, options={})
147
- if hosts_config.is_a?(Hash)
148
- hosts = [ hosts_config ]
149
- else
150
- if hosts_config.is_a?(String) && hosts_config.include?(',')
151
- hosts = hosts_config.split(/\s*,\s*/)
152
- else
153
- hosts = Array(hosts_config)
154
- end
155
- end
156
-
157
- result = hosts.map do |host|
158
- host_parts = case host
159
- when String
160
- if host =~ /^[a-z]+\:\/\//
161
- uri = URI.parse(host)
162
- { :scheme => uri.scheme, :user => uri.user, :password => uri.password, :host => uri.host, :path => uri.path, :port => uri.port }
163
- else
164
- host, port = host.split(':')
165
- { :host => host, :port => port }
166
- end
167
- when URI
168
- { :scheme => host.scheme, :user => host.user, :password => host.password, :host => host.host, :path => host.path, :port => host.port }
169
- when Hash
170
- host
171
- else
172
- raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
173
- end
174
-
175
- host_parts[:port] = host_parts[:port].to_i unless host_parts[:port].nil?
268
+ def __extract_hosts(hosts_config)
269
+ hosts = case hosts_config
270
+ when String
271
+ hosts_config.split(',').map { |h| h.strip! || h }
272
+ when Array
273
+ hosts_config
274
+ when Hash, URI
275
+ [ hosts_config ]
276
+ else
277
+ Array(hosts_config)
278
+ end
176
279
 
177
- # Transfer the selected host parts such as authentication credentials to `options`,
178
- # so we can re-use them when reloading connections
179
- #
180
- host_parts.select { |k,v| [:scheme, :port, :user, :password].include?(k) }.each do |k,v|
181
- @arguments[:http][k] ||= v
182
- end
280
+ host_list = hosts.map { |host| __parse_host(host) }
281
+ @options[:randomize_hosts] ? host_list.shuffle! : host_list
282
+ end
183
283
 
184
- # Remove the trailing slash
185
- host_parts[:path].chomp!('/') if host_parts[:path]
284
+ def __parse_host(host)
285
+ host_parts = case host
286
+ when String
287
+ if host =~ /^[a-z]+\:\/\//
288
+ uri = URI.parse(host)
289
+ { :scheme => uri.scheme,
290
+ :user => uri.user,
291
+ :password => uri.password,
292
+ :host => uri.host,
293
+ :path => uri.path,
294
+ :port => uri.port }
295
+ else
296
+ host, port = host.split(':')
297
+ { :host => host,
298
+ :port => port }
299
+ end
300
+ when URI
301
+ { :scheme => host.scheme,
302
+ :user => host.user,
303
+ :password => host.password,
304
+ :host => host.host,
305
+ :path => host.path,
306
+ :port => host.port }
307
+ when Hash
308
+ host
309
+ else
310
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
311
+ end
186
312
 
187
- host_parts
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]
188
320
  end
189
321
 
190
- result.shuffle! if options[:randomize_hosts]
191
- result
322
+ host_parts[:port] = host_parts[:port].to_i if host_parts[:port]
323
+ host_parts[:path].chomp!('/') if host_parts[:path]
324
+ host_parts
192
325
  end
193
326
 
194
327
  # Auto-detect the best adapter (HTTP "driver") available, based on libraries
@@ -213,6 +346,13 @@ module Elasticsearch
213
346
  ::Faraday.default_adapter
214
347
  end
215
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
216
356
  end
217
357
  end
218
358
  end
@@ -0,0 +1,79 @@
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
+
5
+ # Licensed to Elasticsearch B.V. under one or more contributor
6
+ # license agreements. See the NOTICE file distributed with
7
+ # this work for additional information regarding copyright
8
+ # ownership. Elasticsearch B.V. licenses this file to you under
9
+ # the Apache License, Version 2.0 (the "License"); you may
10
+ # not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing,
16
+ # software distributed under the License is distributed on an
17
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
+ # KIND, either express or implied. See the License for the
19
+ # specific language governing permissions and limitations
20
+ # under the License.
21
+
22
+ module Elasticsearch
23
+ module Transport
24
+
25
+ # Class for wrapping a hash that could have sensitive data.
26
+ # When printed, the sensitive values will be redacted.
27
+ #
28
+ # @since 6.2.0
29
+ class Redacted < ::Hash
30
+
31
+ def initialize(elements = nil)
32
+ super()
33
+ (elements || {}).each_pair{ |key, value| self[key] = value }
34
+ end
35
+
36
+ # The keys whose values will be redacted.
37
+ #
38
+ # @since 6.2.0
39
+ SENSITIVE_KEYS = [ :password,
40
+ :pwd ].freeze
41
+
42
+ # The replacement string used in place of the value for sensitive keys.
43
+ #
44
+ # @since 6.2.0
45
+ STRING_REPLACEMENT = '<REDACTED>'.freeze
46
+
47
+ # Get a string representation of the hash.
48
+ #
49
+ # @return [ String ] The string representation of the hash.
50
+ #
51
+ # @since 6.2.0
52
+ def inspect
53
+ redacted_string(:inspect)
54
+ end
55
+
56
+ # Get a string representation of the hash.
57
+ #
58
+ # @return [ String ] The string representation of the hash.
59
+ #
60
+ # @since 6.2.0
61
+ def to_s
62
+ redacted_string(:to_s)
63
+ end
64
+
65
+ private
66
+
67
+ def redacted_string(method)
68
+ '{' + reduce([]) do |list, (k, v)|
69
+ list << "#{k.send(method)}=>#{redact(k, v, method)}"
70
+ end.join(', ') + '}'
71
+ end
72
+
73
+ def redact(k, v, method)
74
+ return STRING_REPLACEMENT if SENSITIVE_KEYS.include?(k.to_sym)
75
+ v.send(method)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -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
  module Transport
@@ -16,7 +20,7 @@ module Elasticsearch
16
20
  attr_reader :hosts, :options, :connections, :counter, :last_request_at, :protocol
17
21
  attr_accessor :serializer, :sniffer, :logger, :tracer,
18
22
  :reload_connections, :reload_after,
19
- :resurrect_after, :max_retries
23
+ :resurrect_after
20
24
 
21
25
  # Creates a new transport object
22
26
  #
@@ -32,7 +36,7 @@ module Elasticsearch
32
36
  @state_mutex = Mutex.new
33
37
 
34
38
  @hosts = arguments[:hosts] || []
35
- @options = arguments[:options] || {}
39
+ @options = arguments[:options] ? arguments[:options].dup : {}
36
40
  @options[:http] ||= {}
37
41
  @options[:retry_on_status] ||= []
38
42
 
@@ -52,7 +56,6 @@ module Elasticsearch
52
56
  @reload_connections = options[:reload_connections]
53
57
  @reload_after = options[:reload_connections].is_a?(Integer) ? options[:reload_connections] : DEFAULT_RELOAD_AFTER
54
58
  @resurrect_after = options[:resurrect_after] || DEFAULT_RESURRECT_AFTER
55
- @max_retries = options[:retry_on_failure].is_a?(Integer) ? options[:retry_on_failure] : DEFAULT_MAX_RETRIES
56
59
  @retry_on_status = Array(options[:retry_on_status]).map { |d| d.to_i }
57
60
  end
58
61
 
@@ -110,7 +113,7 @@ module Elasticsearch
110
113
 
111
114
  new_connections = __build_connections
112
115
  stale_connections = @connections.all.select { |c| ! new_connections.include?(c) }
113
- new_connections = new_connections.reject { |c| @connections.include?(c) }
116
+ new_connections = new_connections.reject { |c| @connections.all.include?(c) }
114
117
 
115
118
  @connections.remove(stale_connections)
116
119
  @connections.add(new_connections)
@@ -129,7 +132,7 @@ module Elasticsearch
129
132
  Connections::Collection.new \
130
133
  :connections => hosts.map { |host|
131
134
  host[:protocol] = host[:scheme] || options[:scheme] || options[:http][:scheme] || DEFAULT_PROTOCOL
132
- host[:port] ||= options[:port] || options[:http][:scheme] || DEFAULT_PORT
135
+ host[:port] ||= options[:port] || options[:http][:port] || DEFAULT_PORT
133
136
  if (options[:user] || options[:http][:user]) && !host[:user]
134
137
  host[:user] ||= options[:user] || options[:http][:user]
135
138
  host[:password] ||= options[:password] || options[:http][:password]
@@ -184,11 +187,14 @@ module Elasticsearch
184
187
  #
185
188
  # @api private
186
189
  #
187
- def __trace(method, path, params, body, url, response, json, took, duration)
190
+ def __trace(method, path, params, headers, body, url, response, json, took, duration)
188
191
  trace_url = "http://localhost:9200/#{path}?pretty" +
189
192
  ( params.empty? ? '' : "&#{::Faraday::Utils::ParamsHash[params].to_query}" )
190
193
  trace_body = body ? " -d '#{__convert_to_json(body, :pretty => true)}'" : ''
191
- tracer.info "curl -X #{method.to_s.upcase} '#{trace_url}'#{trace_body}\n"
194
+ trace_command = "curl -X #{method.to_s.upcase}"
195
+ trace_command += " -H '#{headers.inject('') { |memo,item| memo << item[0] + ': ' + item[1] }}'" if headers && !headers.empty?
196
+ trace_command += " '#{trace_url}'#{trace_body}\n"
197
+ tracer.info trace_command
192
198
  tracer.debug "# #{Time.now.iso8601} [#{response.status}] (#{format('%.3f', duration)}s)\n#"
193
199
  tracer.debug json ? serializer.dump(json, :pretty => true).gsub(/^/, '# ').sub(/\}$/, "\n# }")+"\n" : "# #{response.body}\n"
194
200
  end
@@ -233,6 +239,7 @@ module Elasticsearch
233
239
  # @param path [String] The API endpoint
234
240
  # @param params [Hash] Request parameters (will be serialized by {Connections::Connection#full_url})
235
241
  # @param body [Hash] Request body (will be serialized by the {#serializer})
242
+ # @param headers [Hash] Request headers (will be serialized by the {#serializer})
236
243
  # @param block [Proc] Code block to evaluate, passed from the implementation
237
244
  #
238
245
  # @return [Response]
@@ -240,10 +247,17 @@ module Elasticsearch
240
247
  # @raise [ServerError] If request failed on server
241
248
  # @raise [Error] If no connection is available
242
249
  #
243
- def perform_request(method, path, params={}, body=nil, &block)
250
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={}, &block)
244
251
  raise NoMethodError, "Implement this method in your transport class" unless block_given?
245
252
  start = Time.now if logger || tracer
246
253
  tries = 0
254
+ reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
255
+
256
+ max_retries = if opts.key?(:retry_on_failure)
257
+ opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
258
+ elsif options.key?(:retry_on_failure)
259
+ options[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : options[:retry_on_failure]
260
+ end
247
261
 
248
262
  params = params.clone
249
263
 
@@ -267,9 +281,9 @@ module Elasticsearch
267
281
  __raise_transport_error(response) if response.status.to_i >= 300 && @retry_on_status.include?(response.status.to_i)
268
282
 
269
283
  rescue Elasticsearch::Transport::Transport::ServerError => e
270
- if @retry_on_status.include?(response.status)
284
+ if response && @retry_on_status.include?(response.status)
271
285
  logger.warn "[#{e.class}] Attempt #{tries} to get response from #{url}" if logger
272
- if tries <= max_retries
286
+ if tries <= (max_retries || DEFAULT_MAX_RETRIES)
273
287
  retry
274
288
  else
275
289
  logger.fatal "[#{e.class}] Cannot get response from #{url} after #{tries} tries" if logger
@@ -284,12 +298,12 @@ module Elasticsearch
284
298
 
285
299
  connection.dead!
286
300
 
287
- if @options[:reload_on_failure] and tries < connections.all.size
301
+ if reload_on_failure and tries < connections.all.size
288
302
  logger.warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})" if logger
289
303
  reload_connections! and retry
290
304
  end
291
305
 
292
- if @options[:retry_on_failure]
306
+ if max_retries
293
307
  logger.warn "[#{e.class}] Attempt #{tries} connecting to #{connection.host.inspect}" if logger
294
308
  if tries <= max_retries
295
309
  retry
@@ -311,7 +325,7 @@ module Elasticsearch
311
325
 
312
326
  if response.status.to_i >= 300
313
327
  __log method, path, params, body, url, response, nil, 'N/A', duration if logger
314
- __trace method, path, params, body, url, response, nil, 'N/A', duration if tracer
328
+ __trace method, path, params, headers, body, url, response, nil, 'N/A', duration if tracer
315
329
 
316
330
  # Log the failure only when `ignore` doesn't match the response status
317
331
  __log_failed response if logger && !ignore.include?(response.status.to_i)
@@ -323,7 +337,7 @@ module Elasticsearch
323
337
  took = (json['took'] ? sprintf('%.3fs', json['took']/1000.0) : 'n/a') rescue 'n/a' if logger || tracer
324
338
 
325
339
  __log method, path, params, body, url, response, json, took, duration if logger && !ignore.include?(response.status.to_i)
326
- __trace method, path, params, body, url, response, json, took, duration if tracer
340
+ __trace method, path, params, headers, body, url, response, json, took, duration if tracer
327
341
 
328
342
  Response.new response.status, json || response.body, response.headers
329
343
  ensure
@@ -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
  module 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
  module Elasticsearch
2
6
  module Transport
3
7
  module Transport
@@ -23,7 +27,7 @@ module Elasticsearch
23
27
  # @option arguments [Hash] :options Options (usually passed in from transport)
24
28
  #
25
29
  def initialize(arguments={})
26
- @host = arguments[:host]
30
+ @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
27
31
  @connection = arguments[:connection]
28
32
  @options = arguments[:options] || {}
29
33
  @state_mutex = Mutex.new
@@ -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
  module 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
  module Elasticsearch
2
6
  module Transport
3
7
  module 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
  module Elasticsearch
2
6
  module Transport
3
7
  module Transport
@@ -15,8 +19,8 @@ module Elasticsearch
15
19
  # @return [Response]
16
20
  # @see Transport::Base#perform_request
17
21
  #
18
- def perform_request(method, path, params={}, body=nil)
19
- super do |connection,url|
22
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
23
+ super do |connection, url|
20
24
  connection.connection.url = url
21
25
 
22
26
  case method
@@ -26,6 +30,7 @@ module Elasticsearch
26
30
  connection.connection.set :nobody, false
27
31
 
28
32
  connection.connection.put_data = __convert_to_json(body) if body
33
+ connection.connection.headers = headers if headers
29
34
  else raise ArgumentError, "Unsupported HTTP method: #{method}"
30
35
  end
31
36
 
@@ -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
  module Transport
@@ -16,15 +20,14 @@ module Elasticsearch
16
20
  # @return [Response]
17
21
  # @see Transport::Base#perform_request
18
22
  #
19
- def perform_request(method, path, params={}, body=nil)
23
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
20
24
  super do |connection, url|
21
- headers = connection.connection.headers
25
+ headers = headers || connection.connection.headers
22
26
 
23
- response = connection.connection.run_request \
24
- method.downcase.to_sym,
25
- url,
26
- ( body ? __convert_to_json(body) : nil ),
27
- headers
27
+ response = connection.connection.run_request(method.downcase.to_sym,
28
+ url,
29
+ ( body ? __convert_to_json(body) : nil ),
30
+ headers)
28
31
 
29
32
  Response.new response.status, response.body, response.headers
30
33
  end
@@ -44,7 +47,7 @@ module Elasticsearch
44
47
  # @return [Array]
45
48
  #
46
49
  def host_unreachable_exceptions
47
- [::Faraday::Error::ConnectionFailed, ::Faraday::Error::TimeoutError]
50
+ [::Faraday::ConnectionFailed, ::Faraday::TimeoutError]
48
51
  end
49
52
  end
50
53
  end