elasticsearch-transport 7.5.0 → 7.17.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +26 -13
  3. data/README.md +159 -64
  4. data/Rakefile +25 -13
  5. data/elasticsearch-transport.gemspec +57 -63
  6. data/lib/elasticsearch/transport/client.rb +183 -58
  7. data/lib/elasticsearch/transport/meta_header.rb +135 -0
  8. data/lib/elasticsearch/transport/redacted.rb +16 -3
  9. data/lib/elasticsearch/transport/transport/base.rb +69 -30
  10. data/lib/elasticsearch/transport/transport/connections/collection.rb +18 -8
  11. data/lib/elasticsearch/transport/transport/connections/connection.rb +25 -9
  12. data/lib/elasticsearch/transport/transport/connections/selector.rb +16 -3
  13. data/lib/elasticsearch/transport/transport/errors.rb +17 -3
  14. data/lib/elasticsearch/transport/transport/http/curb.rb +60 -35
  15. data/lib/elasticsearch/transport/transport/http/faraday.rb +32 -9
  16. data/lib/elasticsearch/transport/transport/http/manticore.rb +51 -31
  17. data/lib/elasticsearch/transport/transport/loggable.rb +16 -3
  18. data/lib/elasticsearch/transport/transport/response.rb +16 -4
  19. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +16 -3
  20. data/lib/elasticsearch/transport/transport/sniffer.rb +35 -15
  21. data/lib/elasticsearch/transport/version.rb +17 -4
  22. data/lib/elasticsearch/transport.rb +35 -33
  23. data/lib/elasticsearch-transport.rb +16 -3
  24. data/spec/elasticsearch/connections/collection_spec.rb +28 -3
  25. data/spec/elasticsearch/connections/selector_spec.rb +16 -3
  26. data/spec/elasticsearch/transport/base_spec.rb +104 -43
  27. data/spec/elasticsearch/transport/client_spec.rb +727 -163
  28. data/spec/elasticsearch/transport/http/curb_spec.rb +126 -0
  29. data/spec/elasticsearch/transport/http/faraday_spec.rb +141 -0
  30. data/spec/elasticsearch/transport/http/manticore_spec.rb +143 -0
  31. data/spec/elasticsearch/transport/meta_header_spec.rb +301 -0
  32. data/spec/elasticsearch/transport/sniffer_spec.rb +16 -16
  33. data/spec/spec_helper.rb +28 -6
  34. data/test/integration/jruby_test.rb +43 -0
  35. data/test/integration/transport_test.rb +46 -29
  36. data/test/profile/client_benchmark_test.rb +16 -3
  37. data/test/test_helper.rb +22 -25
  38. data/test/unit/connection_test.rb +23 -5
  39. data/test/unit/response_test.rb +18 -5
  40. data/test/unit/serializer_test.rb +16 -3
  41. data/test/unit/transport_base_test.rb +33 -11
  42. data/test/unit/transport_curb_test.rb +16 -4
  43. data/test/unit/transport_faraday_test.rb +18 -5
  44. data/test/unit/transport_manticore_test.rb +258 -158
  45. metadata +80 -71
@@ -1,11 +1,23 @@
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
7
20
  module Transport
8
-
9
21
  # @abstract Module with common functionality for transport implementations.
10
22
  #
11
23
  module Base
@@ -34,13 +46,14 @@ module Elasticsearch
34
46
  #
35
47
  # @see Client#initialize
36
48
  #
37
- def initialize(arguments={}, &block)
49
+ def initialize(arguments = {}, &block)
38
50
  @state_mutex = Mutex.new
39
51
 
40
52
  @hosts = arguments[:hosts] || []
41
53
  @options = arguments[:options] || {}
42
54
  @options[:http] ||= {}
43
55
  @options[:retry_on_status] ||= []
56
+ @options[:delay_on_retry] ||= 0
44
57
 
45
58
  @block = block
46
59
  @compression = !!@options[:compression]
@@ -210,7 +223,7 @@ module Elasticsearch
210
223
  # @api private
211
224
  #
212
225
  def __convert_to_json(o=nil, options={})
213
- o = o.is_a?(String) ? o : serializer.dump(o, options)
226
+ o.is_a?(String) ? o : serializer.dump(o, options)
214
227
  end
215
228
 
216
229
  # Returns a full URL based on information from host
@@ -221,9 +234,9 @@ module Elasticsearch
221
234
  def __full_url(host)
222
235
  url = "#{host[:protocol]}://"
223
236
  url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
224
- url += "#{host[:host]}"
237
+ url += host[:host]
225
238
  url += ":#{host[:port]}" if host[:port]
226
- url += "#{host[:path]}" if host[:path]
239
+ url += host[:path] if host[:path]
227
240
  url
228
241
  end
229
242
 
@@ -245,11 +258,13 @@ module Elasticsearch
245
258
  # @raise [ServerError] If request failed on server
246
259
  # @raise [Error] If no connection is available
247
260
  #
248
- def perform_request(method, path, params={}, body=nil, headers=nil, opts={}, &block)
249
- raise NoMethodError, "Implement this method in your transport class" unless block_given?
261
+ def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {}, &block)
262
+ raise NoMethodError, 'Implement this method in your transport class' unless block_given?
263
+
250
264
  start = Time.now
251
265
  tries = 0
252
266
  reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
267
+ delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
253
268
 
254
269
  max_retries = if opts.key?(:retry_on_failure)
255
270
  opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
@@ -258,21 +273,19 @@ module Elasticsearch
258
273
  end
259
274
 
260
275
  params = params.clone
261
-
262
276
  ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
263
277
 
264
278
  begin
279
+ sleep(delay_on_retry / 1000.0) if tries > 0
265
280
  tries += 1
266
- connection = get_connection or raise Error.new("Cannot get new connection from pool.")
281
+ connection = get_connection or raise Error.new('Cannot get new connection from pool.')
267
282
 
268
283
  if connection.connection.respond_to?(:params) && connection.connection.params.respond_to?(:to_hash)
269
284
  params = connection.connection.params.merge(params.to_hash)
270
285
  end
271
286
 
272
- url = connection.full_url(path, params)
273
-
274
- response = block.call(connection, url)
275
-
287
+ url = connection.full_url(path, params)
288
+ response = block.call(connection, url)
276
289
  connection.healthy! if connection.failures > 0
277
290
 
278
291
  # Raise an exception so we can catch it for `retry_on_status`
@@ -295,7 +308,6 @@ module Elasticsearch
295
308
  log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
296
309
 
297
310
  connection.dead!
298
-
299
311
  if reload_on_failure and tries < connections.all.size
300
312
  log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
301
313
  reload_connections! and retry
@@ -322,14 +334,10 @@ module Elasticsearch
322
334
  duration = Time.now - start
323
335
 
324
336
  if response.status.to_i >= 300
325
- __log_response method, path, params, body, url, response, nil, 'N/A', duration
326
- __trace method, path, params, connection.connection.headers, body, url, response, nil, 'N/A', duration if tracer
327
-
337
+ __log_response(method, path, params, body, url, response, nil, 'N/A', duration)
338
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
328
339
  # Log the failure only when `ignore` doesn't match the response status
329
- unless ignore.include?(response.status.to_i)
330
- log_fatal "[#{response.status}] #{response.body}"
331
- end
332
-
340
+ log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i)
333
341
  __raise_transport_error response unless ignore.include?(response.status.to_i)
334
342
  end
335
343
 
@@ -340,8 +348,8 @@ module Elasticsearch
340
348
  __log_response method, path, params, body, url, response, json, took, duration
341
349
  end
342
350
 
343
- __trace method, path, params, connection.connection.headers, body, url, response, nil, 'N/A', duration if tracer
344
-
351
+ __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
352
+ log_warn(response.headers['warning']) if response.headers&.[]('warning')
345
353
  Response.new response.status, json || response.body, response.headers
346
354
  ensure
347
355
  @last_request_at = Time.now
@@ -360,17 +368,38 @@ module Elasticsearch
360
368
 
361
369
  USER_AGENT_STR = 'User-Agent'.freeze
362
370
  USER_AGENT_REGEX = /user\-?\_?agent/
371
+ ACCEPT_ENCODING = 'Accept-Encoding'.freeze
372
+ CONTENT_ENCODING = 'Content-Encoding'.freeze
363
373
  CONTENT_TYPE_STR = 'Content-Type'.freeze
364
374
  CONTENT_TYPE_REGEX = /content\-?\_?type/
365
375
  DEFAULT_CONTENT_TYPE = 'application/json'.freeze
366
376
  GZIP = 'gzip'.freeze
367
- ACCEPT_ENCODING = 'Accept-Encoding'.freeze
368
377
  GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
369
378
  HEX_STRING_DIRECTIVE = 'H*'.freeze
370
379
  RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
371
380
 
381
+ def compress_request(body, headers)
382
+ if body
383
+ headers ||= {}
384
+
385
+ if gzipped?(body)
386
+ headers[CONTENT_ENCODING] = GZIP
387
+ elsif use_compression?
388
+ headers[CONTENT_ENCODING] = GZIP
389
+ gzip = Zlib::GzipWriter.new(StringIO.new)
390
+ gzip << body
391
+ body = gzip.close.string
392
+ else
393
+ headers.delete(CONTENT_ENCODING)
394
+ end
395
+ elsif headers
396
+ headers.delete(CONTENT_ENCODING)
397
+ end
398
+
399
+ [body, headers]
400
+ end
401
+
372
402
  def decompress_response(body)
373
- return body unless use_compression?
374
403
  return body unless gzipped?(body)
375
404
 
376
405
  io = StringIO.new(body)
@@ -383,6 +412,8 @@ module Elasticsearch
383
412
  end
384
413
 
385
414
  def gzipped?(body)
415
+ return unless body && !body.empty?
416
+
386
417
  body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
387
418
  end
388
419
 
@@ -399,7 +430,7 @@ module Elasticsearch
399
430
  end
400
431
 
401
432
  def find_value(hash, regex)
402
- key_value = hash.find { |k,v| k.to_s.downcase =~ regex }
433
+ key_value = hash.find { |k, _| k.to_s.downcase =~ regex }
403
434
  if key_value
404
435
  hash.delete(key_value[0])
405
436
  key_value[1]
@@ -415,7 +446,15 @@ module Elasticsearch
415
446
  "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
416
447
  end
417
448
  end
449
+
450
+ def connection_headers(connection)
451
+ if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore
452
+ @request_options[:headers]
453
+ else
454
+ connection.connection.headers
455
+ end
456
+ end
418
457
  end
419
458
  end
420
459
  end
421
- end
460
+ 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
@@ -65,16 +78,13 @@ module Elasticsearch
65
78
 
66
79
  # Returns a connection.
67
80
  #
68
- # If there are no alive connections, resurrects a connection with least failures.
81
+ # If there are no alive connections, returns a connection with least failures.
69
82
  # Delegates to selector's `#select` method to get the connection.
70
83
  #
71
84
  # @return [Connection]
72
85
  #
73
86
  def get_connection(options={})
74
- if connections.empty? && dead_connection = dead.sort { |a,b| a.failures <=> b.failures }.first
75
- dead_connection.alive!
76
- end
77
- selector.select(options)
87
+ selector.select(options) || @connections.min_by(&:failures)
78
88
  end
79
89
 
80
90
  def each(&block)
@@ -1,12 +1,24 @@
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
7
20
  module Transport
8
21
  module Connections
9
-
10
22
  # Wraps the connection information and logic.
11
23
  #
12
24
  # The Connection instance wraps the host information (hostname, port, attributes, etc),
@@ -21,6 +33,7 @@ module Elasticsearch
21
33
  DEFAULT_RESURRECT_TIMEOUT = 60
22
34
 
23
35
  attr_reader :host, :connection, :options, :failures, :dead_since
36
+ attr_accessor :verified
24
37
 
25
38
  # @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`)
26
39
  # @option arguments [Object] :connection The transport-specific physical connection or "session"
@@ -30,6 +43,7 @@ module Elasticsearch
30
43
  @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
31
44
  @connection = arguments[:connection]
32
45
  @options = arguments[:options] || {}
46
+ @verified = false
33
47
  @state_mutex = Mutex.new
34
48
 
35
49
  @options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT
@@ -41,12 +55,14 @@ module Elasticsearch
41
55
  #
42
56
  # @return [String]
43
57
  #
44
- def full_url(path, params={})
58
+ def full_url(path, params = {})
45
59
  url = "#{host[:protocol]}://"
46
60
  url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
47
61
  url += "#{host[:host]}:#{host[:port]}"
48
62
  url += "#{host[:path]}" if host[:path]
49
- url += "/#{full_path(path, params)}"
63
+ full_path = full_path(path, params)
64
+ url += '/' unless full_path.match?(/^\//)
65
+ url += full_path
50
66
  end
51
67
 
52
68
  # Returns the complete endpoint path with serialized parameters.
@@ -122,14 +138,15 @@ module Elasticsearch
122
138
  }
123
139
  end
124
140
 
125
- # Equality operator based on connection protocol, host and port
141
+ # Equality operator based on connection protocol, host, port and attributes
126
142
  #
127
143
  # @return [Boolean]
128
144
  #
129
145
  def ==(other)
130
146
  self.host[:protocol] == other.host[:protocol] && \
131
147
  self.host[:host] == other.host[:host] && \
132
- self.host[:port].to_i == other.host[:port].to_i
148
+ self.host[:port].to_i == other.host[:port].to_i && \
149
+ self.host[:attributes] == other.host[:attributes]
133
150
  end
134
151
 
135
152
  # @return [String]
@@ -138,7 +155,6 @@ module Elasticsearch
138
155
  "<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
139
156
  end
140
157
  end
141
-
142
158
  end
143
159
  end
144
160
  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
@@ -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
@@ -51,6 +64,7 @@ module Elasticsearch
51
64
  418 => 'ImATeapot',
52
65
  421 => 'TooManyConnectionsFromThisIP',
53
66
  426 => 'UpgradeRequired',
67
+ 429 => 'TooManyRequests',
54
68
  450 => 'BlockedByWindowsParentalControls',
55
69
  494 => 'RequestHeaderTooLarge',
56
70
  497 => 'HTTPToHTTPS',
@@ -1,56 +1,82 @@
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
7
20
  module Transport
8
21
  module HTTP
9
-
10
22
  # Alternative HTTP transport implementation, using the [_Curb_](https://rubygems.org/gems/curb) client.
11
23
  #
12
24
  # @see Transport::Base
13
25
  #
14
26
  class Curb
15
27
  include Base
16
-
17
28
  # Performs the request by invoking {Transport::Base#perform_request} with a block.
18
29
  #
19
30
  # @return [Response]
20
31
  # @see Transport::Base#perform_request
21
32
  #
22
33
  def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
23
- super do |connection, url|
34
+ super do |connection, _url|
24
35
  connection.connection.url = connection.full_url(path, params)
36
+ body = body ? __convert_to_json(body) : nil
37
+ body, headers = compress_request(body, headers)
25
38
 
26
39
  case method
27
- when 'HEAD'
28
- connection.connection.set :nobody, true
29
- when 'GET', 'POST', 'PUT', 'DELETE'
30
- connection.connection.set :nobody, false
31
-
32
- connection.connection.put_data = __convert_to_json(body) if body
33
-
34
- if headers
35
- if connection.connection.headers
36
- connection.connection.headers.merge!(headers)
37
- else
38
- connection.connection.headers = headers
39
- 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
40
52
  end
41
-
42
- else raise ArgumentError, "Unsupported HTTP method: #{method}"
53
+ end
54
+ else
55
+ raise ArgumentError, "Unsupported HTTP method: #{method}"
43
56
  end
44
57
 
45
58
  connection.connection.http(method.to_sym)
46
59
 
47
- response_headers = {}
48
- 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?
49
71
 
50
- Response.new connection.connection.response_code,
51
- decompress_response(connection.connection.body_str),
52
- 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'
53
78
  end
79
+ headers_hash
54
80
  end
55
81
 
56
82
  # Builds and returns a connection
@@ -59,9 +85,8 @@ module Elasticsearch
59
85
  #
60
86
  def __build_connection(host, options={}, block=nil)
61
87
  client = ::Curl::Easy.new
62
-
63
88
  apply_headers(client, options)
64
- client.url = __full_url(host)
89
+ client.url = __full_url(host)
65
90
 
66
91
  if host[:user]
67
92
  client.http_auth_types = host[:auth_type] || :basic
@@ -93,13 +118,13 @@ module Elasticsearch
93
118
 
94
119
  def user_agent_header(client)
95
120
  @user_agent ||= begin
96
- meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
97
- if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
98
- meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
99
- end
100
- meta << "Curb #{Curl::CURB_VERSION}"
101
- "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
102
- 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
103
128
  end
104
129
  end
105
130
  end
@@ -1,12 +1,24 @@
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
7
20
  module Transport
8
21
  module HTTP
9
-
10
22
  # The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday)
11
23
  # library for abstracting the HTTP client.
12
24
  #
@@ -20,13 +32,24 @@ module Elasticsearch
20
32
  # @return [Response]
21
33
  # @see Transport::Base#perform_request
22
34
  #
23
- def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
35
+ def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
24
36
  super do |connection, url|
25
- headers = headers || connection.connection.headers
37
+ headers = if connection.connection.headers
38
+ if !headers.nil?
39
+ connection.connection.headers.merge(headers)
40
+ else
41
+ connection.connection.headers
42
+ end
43
+ else
44
+ headers
45
+ end
46
+
47
+ body = body ? __convert_to_json(body) : nil
48
+ body, headers = compress_request(body, headers)
26
49
 
27
50
  response = connection.connection.run_request(method.downcase.to_sym,
28
51
  url,
29
- ( body ? __convert_to_json(body) : nil ),
52
+ body,
30
53
  headers)
31
54
 
32
55
  Response.new response.status, decompress_response(response.body), response.headers
@@ -40,7 +63,7 @@ module Elasticsearch
40
63
  def __build_connection(host, options={}, block=nil)
41
64
  client = ::Faraday.new(__full_url(host), options, &block)
42
65
  apply_headers(client, options)
43
- Connections::Connection.new :host => host, :connection => client
66
+ Connections::Connection.new(host: host, connection: client)
44
67
  end
45
68
 
46
69
  # Returns an array of implementation specific connection errors.
@@ -48,7 +71,7 @@ module Elasticsearch
48
71
  # @return [Array]
49
72
  #
50
73
  def host_unreachable_exceptions
51
- [::Faraday::Error::ConnectionFailed, ::Faraday::Error::TimeoutError]
74
+ [::Faraday::ConnectionFailed, ::Faraday::TimeoutError]
52
75
  end
53
76
 
54
77
  private