elasticsearch-transport 7.9.0 → 7.17.10
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.
- checksums.yaml +4 -4
- data/Gemfile +10 -10
- data/Gemfile-faraday1.gemfile +47 -0
- data/README.md +23 -17
- data/Rakefile +47 -10
- data/elasticsearch-transport.gemspec +16 -18
- data/lib/elasticsearch/transport/client.rb +133 -56
- data/lib/elasticsearch/transport/meta_header.rb +135 -0
- data/lib/elasticsearch/transport/transport/base.rb +41 -22
- data/lib/elasticsearch/transport/transport/connections/collection.rb +2 -5
- data/lib/elasticsearch/transport/transport/connections/connection.rb +6 -4
- data/lib/elasticsearch/transport/transport/errors.rb +1 -0
- data/lib/elasticsearch/transport/transport/http/curb.rb +44 -32
- data/lib/elasticsearch/transport/transport/http/faraday.rb +15 -5
- data/lib/elasticsearch/transport/transport/http/manticore.rb +41 -29
- data/lib/elasticsearch/transport/transport/response.rb +1 -1
- data/lib/elasticsearch/transport/version.rb +1 -1
- data/lib/elasticsearch/transport.rb +19 -30
- data/spec/elasticsearch/connections/collection_spec.rb +12 -0
- data/spec/elasticsearch/transport/base_spec.rb +90 -33
- data/spec/elasticsearch/transport/client_spec.rb +569 -164
- data/spec/elasticsearch/transport/http/curb_spec.rb +126 -0
- data/spec/elasticsearch/transport/http/faraday_spec.rb +141 -0
- data/spec/elasticsearch/transport/http/manticore_spec.rb +161 -0
- data/spec/elasticsearch/transport/meta_header_spec.rb +301 -0
- data/spec/spec_helper.rb +13 -5
- data/test/integration/jruby_test.rb +43 -0
- data/test/integration/transport_test.rb +93 -43
- data/test/test_helper.rb +10 -22
- data/test/unit/adapters_test.rb +88 -0
- data/test/unit/connection_test.rb +7 -2
- data/test/unit/response_test.rb +1 -1
- data/test/unit/transport_base_test.rb +17 -8
- data/test/unit/transport_curb_test.rb +0 -1
- data/test/unit/transport_faraday_test.rb +2 -2
- data/test/unit/transport_manticore_test.rb +242 -155
- metadata +62 -84
|
@@ -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
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
module Elasticsearch
|
|
19
19
|
module Transport
|
|
20
20
|
module Transport
|
|
21
|
-
|
|
22
21
|
# @abstract Module with common functionality for transport implementations.
|
|
23
22
|
#
|
|
24
23
|
module Base
|
|
@@ -54,6 +53,7 @@ module Elasticsearch
|
|
|
54
53
|
@options = arguments[:options] || {}
|
|
55
54
|
@options[:http] ||= {}
|
|
56
55
|
@options[:retry_on_status] ||= []
|
|
56
|
+
@options[:delay_on_retry] ||= 0
|
|
57
57
|
|
|
58
58
|
@block = block
|
|
59
59
|
@compression = !!@options[:compression]
|
|
@@ -223,7 +223,7 @@ module Elasticsearch
|
|
|
223
223
|
# @api private
|
|
224
224
|
#
|
|
225
225
|
def __convert_to_json(o=nil, options={})
|
|
226
|
-
o
|
|
226
|
+
o.is_a?(String) ? o : serializer.dump(o, options)
|
|
227
227
|
end
|
|
228
228
|
|
|
229
229
|
# Returns a full URL based on information from host
|
|
@@ -264,6 +264,7 @@ module Elasticsearch
|
|
|
264
264
|
start = Time.now
|
|
265
265
|
tries = 0
|
|
266
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])
|
|
267
268
|
|
|
268
269
|
max_retries = if opts.key?(:retry_on_failure)
|
|
269
270
|
opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
|
|
@@ -272,10 +273,10 @@ module Elasticsearch
|
|
|
272
273
|
end
|
|
273
274
|
|
|
274
275
|
params = params.clone
|
|
275
|
-
|
|
276
276
|
ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
|
|
277
277
|
|
|
278
278
|
begin
|
|
279
|
+
sleep(delay_on_retry / 1000.0) if tries > 0
|
|
279
280
|
tries += 1
|
|
280
281
|
connection = get_connection or raise Error.new('Cannot get new connection from pool.')
|
|
281
282
|
|
|
@@ -284,9 +285,7 @@ module Elasticsearch
|
|
|
284
285
|
end
|
|
285
286
|
|
|
286
287
|
url = connection.full_url(path, params)
|
|
287
|
-
|
|
288
288
|
response = block.call(connection, url)
|
|
289
|
-
|
|
290
289
|
connection.healthy! if connection.failures > 0
|
|
291
290
|
|
|
292
291
|
# Raise an exception so we can catch it for `retry_on_status`
|
|
@@ -309,7 +308,6 @@ module Elasticsearch
|
|
|
309
308
|
log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
|
|
310
309
|
|
|
311
310
|
connection.dead!
|
|
312
|
-
|
|
313
311
|
if reload_on_failure and tries < connections.all.size
|
|
314
312
|
log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
|
|
315
313
|
reload_connections! and retry
|
|
@@ -336,14 +334,10 @@ module Elasticsearch
|
|
|
336
334
|
duration = Time.now - start
|
|
337
335
|
|
|
338
336
|
if response.status.to_i >= 300
|
|
339
|
-
__log_response
|
|
340
|
-
__trace
|
|
341
|
-
|
|
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
|
|
342
339
|
# Log the failure only when `ignore` doesn't match the response status
|
|
343
|
-
unless ignore.include?(response.status.to_i)
|
|
344
|
-
log_fatal "[#{response.status}] #{response.body}"
|
|
345
|
-
end
|
|
346
|
-
|
|
340
|
+
log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i)
|
|
347
341
|
__raise_transport_error response unless ignore.include?(response.status.to_i)
|
|
348
342
|
end
|
|
349
343
|
|
|
@@ -354,10 +348,8 @@ module Elasticsearch
|
|
|
354
348
|
__log_response method, path, params, body, url, response, json, took, duration
|
|
355
349
|
end
|
|
356
350
|
|
|
357
|
-
__trace
|
|
358
|
-
|
|
359
|
-
warnings(response.headers['warning']) if response.headers&.[]('warning')
|
|
360
|
-
|
|
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')
|
|
361
353
|
Response.new response.status, json || response.body, response.headers
|
|
362
354
|
ensure
|
|
363
355
|
@last_request_at = Time.now
|
|
@@ -376,17 +368,38 @@ module Elasticsearch
|
|
|
376
368
|
|
|
377
369
|
USER_AGENT_STR = 'User-Agent'.freeze
|
|
378
370
|
USER_AGENT_REGEX = /user\-?\_?agent/
|
|
371
|
+
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
|
372
|
+
CONTENT_ENCODING = 'Content-Encoding'.freeze
|
|
379
373
|
CONTENT_TYPE_STR = 'Content-Type'.freeze
|
|
380
374
|
CONTENT_TYPE_REGEX = /content\-?\_?type/
|
|
381
375
|
DEFAULT_CONTENT_TYPE = 'application/json'.freeze
|
|
382
376
|
GZIP = 'gzip'.freeze
|
|
383
|
-
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
|
384
377
|
GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
|
|
385
378
|
HEX_STRING_DIRECTIVE = 'H*'.freeze
|
|
386
379
|
RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
|
|
387
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
|
+
|
|
388
402
|
def decompress_response(body)
|
|
389
|
-
return body unless use_compression?
|
|
390
403
|
return body unless gzipped?(body)
|
|
391
404
|
|
|
392
405
|
io = StringIO.new(body)
|
|
@@ -399,6 +412,8 @@ module Elasticsearch
|
|
|
399
412
|
end
|
|
400
413
|
|
|
401
414
|
def gzipped?(body)
|
|
415
|
+
return unless body && !body.empty?
|
|
416
|
+
|
|
402
417
|
body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
|
|
403
418
|
end
|
|
404
419
|
|
|
@@ -415,7 +430,7 @@ module Elasticsearch
|
|
|
415
430
|
end
|
|
416
431
|
|
|
417
432
|
def find_value(hash, regex)
|
|
418
|
-
key_value = hash.find { |k,
|
|
433
|
+
key_value = hash.find { |k, _| k.to_s.downcase =~ regex }
|
|
419
434
|
if key_value
|
|
420
435
|
hash.delete(key_value[0])
|
|
421
436
|
key_value[1]
|
|
@@ -432,8 +447,12 @@ module Elasticsearch
|
|
|
432
447
|
end
|
|
433
448
|
end
|
|
434
449
|
|
|
435
|
-
def
|
|
436
|
-
|
|
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
|
|
437
456
|
end
|
|
438
457
|
end
|
|
439
458
|
end
|
|
@@ -78,16 +78,13 @@ module Elasticsearch
|
|
|
78
78
|
|
|
79
79
|
# Returns a connection.
|
|
80
80
|
#
|
|
81
|
-
# If there are no alive connections,
|
|
81
|
+
# If there are no alive connections, returns a connection with least failures.
|
|
82
82
|
# Delegates to selector's `#select` method to get the connection.
|
|
83
83
|
#
|
|
84
84
|
# @return [Connection]
|
|
85
85
|
#
|
|
86
86
|
def get_connection(options={})
|
|
87
|
-
|
|
88
|
-
dead_connection.alive!
|
|
89
|
-
end
|
|
90
|
-
selector.select(options)
|
|
87
|
+
selector.select(options) || @connections.min_by(&:failures)
|
|
91
88
|
end
|
|
92
89
|
|
|
93
90
|
def each(&block)
|
|
@@ -19,7 +19,6 @@ module Elasticsearch
|
|
|
19
19
|
module Transport
|
|
20
20
|
module Transport
|
|
21
21
|
module Connections
|
|
22
|
-
|
|
23
22
|
# Wraps the connection information and logic.
|
|
24
23
|
#
|
|
25
24
|
# The Connection instance wraps the host information (hostname, port, attributes, etc),
|
|
@@ -34,6 +33,7 @@ module Elasticsearch
|
|
|
34
33
|
DEFAULT_RESURRECT_TIMEOUT = 60
|
|
35
34
|
|
|
36
35
|
attr_reader :host, :connection, :options, :failures, :dead_since
|
|
36
|
+
attr_accessor :verified
|
|
37
37
|
|
|
38
38
|
# @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`)
|
|
39
39
|
# @option arguments [Object] :connection The transport-specific physical connection or "session"
|
|
@@ -43,6 +43,7 @@ module Elasticsearch
|
|
|
43
43
|
@host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
|
|
44
44
|
@connection = arguments[:connection]
|
|
45
45
|
@options = arguments[:options] || {}
|
|
46
|
+
@verified = false
|
|
46
47
|
@state_mutex = Mutex.new
|
|
47
48
|
|
|
48
49
|
@options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT
|
|
@@ -54,12 +55,14 @@ module Elasticsearch
|
|
|
54
55
|
#
|
|
55
56
|
# @return [String]
|
|
56
57
|
#
|
|
57
|
-
def full_url(path, params={})
|
|
58
|
+
def full_url(path, params = {})
|
|
58
59
|
url = "#{host[:protocol]}://"
|
|
59
60
|
url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
|
|
60
61
|
url += "#{host[:host]}:#{host[:port]}"
|
|
61
62
|
url += "#{host[:path]}" if host[:path]
|
|
62
|
-
|
|
63
|
+
full_path = full_path(path, params)
|
|
64
|
+
url += '/' unless full_path.match?(/^\//)
|
|
65
|
+
url += full_path
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
# Returns the complete endpoint path with serialized parameters.
|
|
@@ -152,7 +155,6 @@ module Elasticsearch
|
|
|
152
155
|
"<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
|
|
153
156
|
end
|
|
154
157
|
end
|
|
155
|
-
|
|
156
158
|
end
|
|
157
159
|
end
|
|
158
160
|
end
|
|
@@ -19,51 +19,64 @@ module Elasticsearch
|
|
|
19
19
|
module Transport
|
|
20
20
|
module Transport
|
|
21
21
|
module HTTP
|
|
22
|
-
|
|
23
22
|
# Alternative HTTP transport implementation, using the [_Curb_](https://rubygems.org/gems/curb) client.
|
|
24
23
|
#
|
|
25
24
|
# @see Transport::Base
|
|
26
25
|
#
|
|
27
26
|
class Curb
|
|
28
27
|
include Base
|
|
29
|
-
|
|
30
28
|
# Performs the request by invoking {Transport::Base#perform_request} with a block.
|
|
31
29
|
#
|
|
32
30
|
# @return [Response]
|
|
33
31
|
# @see Transport::Base#perform_request
|
|
34
32
|
#
|
|
35
33
|
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
|
36
|
-
super do |connection,
|
|
34
|
+
super do |connection, _url|
|
|
37
35
|
connection.connection.url = connection.full_url(path, params)
|
|
36
|
+
body = body ? __convert_to_json(body) : nil
|
|
37
|
+
body, headers = compress_request(body, headers)
|
|
38
38
|
|
|
39
39
|
case method
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
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
|
|
53
52
|
end
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
raise ArgumentError, "Unsupported HTTP method: #{method}"
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
connection.connection.http(method.to_sym)
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
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?
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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'
|
|
66
78
|
end
|
|
79
|
+
headers_hash
|
|
67
80
|
end
|
|
68
81
|
|
|
69
82
|
# Builds and returns a connection
|
|
@@ -72,9 +85,8 @@ module Elasticsearch
|
|
|
72
85
|
#
|
|
73
86
|
def __build_connection(host, options={}, block=nil)
|
|
74
87
|
client = ::Curl::Easy.new
|
|
75
|
-
|
|
76
88
|
apply_headers(client, options)
|
|
77
|
-
client.url
|
|
89
|
+
client.url = __full_url(host)
|
|
78
90
|
|
|
79
91
|
if host[:user]
|
|
80
92
|
client.http_auth_types = host[:auth_type] || :basic
|
|
@@ -106,13 +118,13 @@ module Elasticsearch
|
|
|
106
118
|
|
|
107
119
|
def user_agent_header(client)
|
|
108
120
|
@user_agent ||= begin
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
116
128
|
end
|
|
117
129
|
end
|
|
118
130
|
end
|
|
@@ -19,7 +19,6 @@ module Elasticsearch
|
|
|
19
19
|
module Transport
|
|
20
20
|
module Transport
|
|
21
21
|
module HTTP
|
|
22
|
-
|
|
23
22
|
# The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday)
|
|
24
23
|
# library for abstracting the HTTP client.
|
|
25
24
|
#
|
|
@@ -33,13 +32,24 @@ module Elasticsearch
|
|
|
33
32
|
# @return [Response]
|
|
34
33
|
# @see Transport::Base#perform_request
|
|
35
34
|
#
|
|
36
|
-
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
|
35
|
+
def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
|
|
37
36
|
super do |connection, url|
|
|
38
|
-
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)
|
|
39
49
|
|
|
40
50
|
response = connection.connection.run_request(method.downcase.to_sym,
|
|
41
51
|
url,
|
|
42
|
-
|
|
52
|
+
body,
|
|
43
53
|
headers)
|
|
44
54
|
|
|
45
55
|
Response.new response.status, decompress_response(response.body), response.headers
|
|
@@ -53,7 +63,7 @@ module Elasticsearch
|
|
|
53
63
|
def __build_connection(host, options={}, block=nil)
|
|
54
64
|
client = ::Faraday.new(__full_url(host), options, &block)
|
|
55
65
|
apply_headers(client, options)
|
|
56
|
-
Connections::Connection.new
|
|
66
|
+
Connections::Connection.new(host: host, connection: client)
|
|
57
67
|
end
|
|
58
68
|
|
|
59
69
|
# Returns an array of implementation specific connection errors.
|
|
@@ -62,13 +62,20 @@ module Elasticsearch
|
|
|
62
62
|
class Manticore
|
|
63
63
|
include Base
|
|
64
64
|
|
|
65
|
-
def initialize(arguments={}, &block)
|
|
65
|
+
def initialize(arguments = {}, &block)
|
|
66
|
+
@request_options = {
|
|
67
|
+
headers: (
|
|
68
|
+
arguments.dig(:transport_options, :headers) ||
|
|
69
|
+
arguments.dig(:options, :transport_options, :headers) ||
|
|
70
|
+
{}
|
|
71
|
+
)
|
|
72
|
+
}
|
|
66
73
|
@manticore = build_client(arguments[:options] || {})
|
|
67
74
|
super(arguments, &block)
|
|
68
75
|
end
|
|
69
76
|
|
|
70
77
|
# Should just be run once at startup
|
|
71
|
-
def build_client(options={})
|
|
78
|
+
def build_client(options = {})
|
|
72
79
|
client_options = options[:transport_options] || {}
|
|
73
80
|
client_options[:ssl] = options[:ssl] || {}
|
|
74
81
|
|
|
@@ -80,26 +87,28 @@ module Elasticsearch
|
|
|
80
87
|
# @return [Response]
|
|
81
88
|
# @see Transport::Base#perform_request
|
|
82
89
|
#
|
|
83
|
-
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
|
90
|
+
def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
|
|
84
91
|
super do |connection, url|
|
|
85
|
-
|
|
92
|
+
body = body ? __convert_to_json(body) : nil
|
|
93
|
+
body, headers = compress_request(body, parse_headers(headers))
|
|
94
|
+
params[:body] = body if body
|
|
86
95
|
params[:headers] = headers if headers
|
|
87
|
-
|
|
96
|
+
|
|
88
97
|
case method
|
|
89
|
-
when
|
|
98
|
+
when 'GET'
|
|
90
99
|
resp = connection.connection.get(url, params)
|
|
91
|
-
when
|
|
100
|
+
when 'HEAD'
|
|
92
101
|
resp = connection.connection.head(url, params)
|
|
93
|
-
when
|
|
102
|
+
when 'PUT'
|
|
94
103
|
resp = connection.connection.put(url, params)
|
|
95
|
-
when
|
|
104
|
+
when 'POST'
|
|
96
105
|
resp = connection.connection.post(url, params)
|
|
97
|
-
when
|
|
106
|
+
when 'DELETE'
|
|
98
107
|
resp = connection.connection.delete(url, params)
|
|
99
108
|
else
|
|
100
109
|
raise ArgumentError.new "Method #{method} not supported"
|
|
101
110
|
end
|
|
102
|
-
Response.new
|
|
111
|
+
Response.new(resp.code, resp.read_body, resp.headers)
|
|
103
112
|
end
|
|
104
113
|
end
|
|
105
114
|
|
|
@@ -109,24 +118,21 @@ module Elasticsearch
|
|
|
109
118
|
# @return [Connections::Collection]
|
|
110
119
|
#
|
|
111
120
|
def __build_connections
|
|
112
|
-
|
|
113
|
-
apply_headers(@request_options, options[:transport_options])
|
|
114
|
-
apply_headers(@request_options, options)
|
|
121
|
+
apply_headers(options)
|
|
115
122
|
|
|
116
|
-
Connections::Collection.new
|
|
117
|
-
:
|
|
118
|
-
host[:protocol]
|
|
119
|
-
host[:port]
|
|
123
|
+
Connections::Collection.new(
|
|
124
|
+
connections: hosts.map do |host|
|
|
125
|
+
host[:protocol] = host[:scheme] || DEFAULT_PROTOCOL
|
|
126
|
+
host[:port] ||= DEFAULT_PORT
|
|
120
127
|
|
|
121
128
|
host.delete(:user) # auth is not supported here.
|
|
122
129
|
host.delete(:password) # use the headers
|
|
123
130
|
|
|
124
|
-
Connections::Connection.new
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
:selector => options[:selector]
|
|
131
|
+
Connections::Connection.new(host: host, connection: @manticore)
|
|
132
|
+
end,
|
|
133
|
+
selector_class: options[:selector_class],
|
|
134
|
+
selector: options[:selector]
|
|
135
|
+
)
|
|
130
136
|
end
|
|
131
137
|
|
|
132
138
|
# Closes all connections by marking them as dead
|
|
@@ -154,16 +160,22 @@ module Elasticsearch
|
|
|
154
160
|
|
|
155
161
|
private
|
|
156
162
|
|
|
157
|
-
def
|
|
158
|
-
|
|
163
|
+
def parse_headers(headers)
|
|
164
|
+
request_headers = @request_options.fetch(:headers, {})
|
|
165
|
+
headers = request_headers.merge(headers || {})
|
|
166
|
+
headers.empty? ? nil : headers
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def apply_headers(options)
|
|
170
|
+
headers = options[:headers].clone || options.dig(:transport_options, :headers).clone || {}
|
|
159
171
|
headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
|
|
160
|
-
headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header
|
|
172
|
+
headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || find_value(@request_options[:headers], USER_AGENT_REGEX) || user_agent_header
|
|
161
173
|
headers[ACCEPT_ENCODING] = GZIP if use_compression?
|
|
162
|
-
request_options.merge!(headers
|
|
174
|
+
@request_options[:headers].merge!(headers)
|
|
163
175
|
end
|
|
164
176
|
|
|
165
177
|
def user_agent_header
|
|
166
|
-
@
|
|
178
|
+
@user_agent_header ||= begin
|
|
167
179
|
meta = ["RUBY_VERSION: #{JRUBY_VERSION}"]
|
|
168
180
|
if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
|
|
169
181
|
meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
|
|
@@ -28,7 +28,7 @@ module Elasticsearch
|
|
|
28
28
|
# @param headers [Hash] Response headers
|
|
29
29
|
def initialize(status, body, headers={})
|
|
30
30
|
@status, @body, @headers = status, body, headers
|
|
31
|
-
@body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding)
|
|
31
|
+
@body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding) && !body.frozen?
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|