elasticsearch-transport 7.5.0 → 7.17.7

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 (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