elasticsearch-transport 7.13.3 → 7.17.11
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 +9 -9
- data/Gemfile-faraday1.gemfile +47 -0
- data/README.md +3 -8
- data/Rakefile +47 -10
- data/elasticsearch-transport.gemspec +17 -18
- data/lib/elasticsearch/transport/client.rb +34 -3
- data/lib/elasticsearch/transport/transport/base.rb +41 -22
- data/lib/elasticsearch/transport/transport/connections/connection.rb +2 -1
- 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 +5 -3
- 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/transport/base_spec.rb +50 -9
- data/spec/elasticsearch/transport/client_spec.rb +138 -64
- 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 +66 -30
- data/spec/spec_helper.rb +13 -5
- data/test/integration/jruby_test.rb +43 -0
- data/test/integration/transport_test.rb +89 -52
- data/test/test_helper.rb +10 -22
- data/test/unit/adapters_test.rb +88 -0
- data/test/unit/response_test.rb +1 -1
- data/test/unit/transport_base_test.rb +16 -7
- data/test/unit/transport_curb_test.rb +0 -1
- data/test/unit/transport_manticore_test.rb +242 -155
- metadata +68 -79
@@ -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
|
#
|
@@ -45,9 +44,12 @@ module Elasticsearch
|
|
45
44
|
headers
|
46
45
|
end
|
47
46
|
|
47
|
+
body = body ? __convert_to_json(body) : nil
|
48
|
+
body, headers = compress_request(body, headers)
|
49
|
+
|
48
50
|
response = connection.connection.run_request(method.downcase.to_sym,
|
49
51
|
url,
|
50
|
-
|
52
|
+
body,
|
51
53
|
headers)
|
52
54
|
|
53
55
|
Response.new response.status, decompress_response(response.body), response.headers
|
@@ -61,7 +63,7 @@ module Elasticsearch
|
|
61
63
|
def __build_connection(host, options={}, block=nil)
|
62
64
|
client = ::Faraday.new(__full_url(host), options, &block)
|
63
65
|
apply_headers(client, options)
|
64
|
-
Connections::Connection.new
|
66
|
+
Connections::Connection.new(host: host, connection: client)
|
65
67
|
end
|
66
68
|
|
67
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
|
|
@@ -15,35 +15,24 @@
|
|
15
15
|
# specific language governing permissions and limitations
|
16
16
|
# under the License.
|
17
17
|
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
18
|
+
require 'uri'
|
19
|
+
require 'time'
|
20
|
+
require 'timeout'
|
21
|
+
require 'zlib'
|
22
|
+
require 'multi_json'
|
23
|
+
require 'faraday'
|
23
24
|
|
24
|
-
require
|
25
|
-
require
|
26
|
-
require
|
27
|
-
require
|
28
|
-
require
|
29
|
-
require
|
30
|
-
require
|
31
|
-
require
|
32
|
-
require
|
33
|
-
require
|
34
|
-
require
|
35
|
-
require
|
25
|
+
require 'elasticsearch/transport/transport/loggable'
|
26
|
+
require 'elasticsearch/transport/transport/serializer/multi_json'
|
27
|
+
require 'elasticsearch/transport/transport/sniffer'
|
28
|
+
require 'elasticsearch/transport/transport/response'
|
29
|
+
require 'elasticsearch/transport/transport/errors'
|
30
|
+
require 'elasticsearch/transport/transport/base'
|
31
|
+
require 'elasticsearch/transport/transport/connections/selector'
|
32
|
+
require 'elasticsearch/transport/transport/connections/connection'
|
33
|
+
require 'elasticsearch/transport/transport/connections/collection'
|
34
|
+
require 'elasticsearch/transport/transport/http/faraday'
|
35
|
+
require 'elasticsearch/transport/client'
|
36
|
+
require 'elasticsearch/transport/redacted'
|
36
37
|
|
37
|
-
require
|
38
|
-
|
39
|
-
module Elasticsearch
|
40
|
-
module Client
|
41
|
-
|
42
|
-
# A convenience wrapper for {::Elasticsearch::Transport::Client#initialize}.
|
43
|
-
#
|
44
|
-
def new(arguments={}, &block)
|
45
|
-
Elasticsearch::Transport::Client.new(arguments, &block)
|
46
|
-
end
|
47
|
-
extend self
|
48
|
-
end
|
49
|
-
end
|
38
|
+
require 'elasticsearch/transport/version'
|
@@ -32,14 +32,14 @@ describe Elasticsearch::Transport::Transport::Base do
|
|
32
32
|
expect(logger).not_to receive(:error).with(/secret_password/)
|
33
33
|
|
34
34
|
expect {
|
35
|
-
client.
|
35
|
+
client.perform_request('GET', '_cluster/stats')
|
36
36
|
}.to raise_exception(Faraday::ConnectionFailed)
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'replaces the password with the string \'REDACTED\'' do
|
40
40
|
expect(logger).to receive(:error).with(/REDACTED/)
|
41
41
|
expect {
|
42
|
-
client.
|
42
|
+
client.perform_request('GET', '_cluster/stats')
|
43
43
|
}.to raise_exception(Faraday::ConnectionFailed)
|
44
44
|
end
|
45
45
|
end
|
@@ -65,7 +65,27 @@ describe Elasticsearch::Transport::Transport::Base do
|
|
65
65
|
}
|
66
66
|
end
|
67
67
|
|
68
|
-
|
68
|
+
if jruby?
|
69
|
+
let(:client) { Elasticsearch::Transport::Client.new(arguments) }
|
70
|
+
let(:logger) { double('logger', fatal?: true, fatal: '') }
|
71
|
+
|
72
|
+
it 'does not include the password in the logged string' do
|
73
|
+
expect(logger).not_to receive(:fatal).with(/secret_password/)
|
74
|
+
|
75
|
+
expect {
|
76
|
+
client.perform_request('GET', '_cluster/stats')
|
77
|
+
}.to raise_exception(Faraday::SSLError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'replaces the password with the string \'REDACTED\'' do
|
81
|
+
expect(logger).to receive(:fatal).with(/REDACTED/)
|
82
|
+
expect {
|
83
|
+
client.perform_request('GET', '_cluster/stats')
|
84
|
+
}.to raise_exception(Faraday::SSLError)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
it_behaves_like 'a redacted string'
|
88
|
+
end
|
69
89
|
end
|
70
90
|
|
71
91
|
context 'when the user and password are provided in the URI object' do
|
@@ -75,8 +95,27 @@ describe Elasticsearch::Transport::Transport::Base do
|
|
75
95
|
logger: logger
|
76
96
|
}
|
77
97
|
end
|
78
|
-
|
79
|
-
|
98
|
+
if jruby?
|
99
|
+
let(:client) { Elasticsearch::Transport::Client.new(arguments) }
|
100
|
+
let(:logger) { double('logger', fatal?: true, fatal: '') }
|
101
|
+
|
102
|
+
it 'does not include the password in the logged string' do
|
103
|
+
expect(logger).not_to receive(:fatal).with(/secret_password/)
|
104
|
+
|
105
|
+
expect {
|
106
|
+
client.perform_request('GET', '_cluster/stats')
|
107
|
+
}.to raise_exception(Faraday::SSLError)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'replaces the password with the string \'REDACTED\'' do
|
111
|
+
expect(logger).to receive(:fatal).with(/REDACTED/)
|
112
|
+
expect {
|
113
|
+
client.perform_request('GET', '_cluster/stats')
|
114
|
+
}.to raise_exception(Faraday::SSLError)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
it_behaves_like 'a redacted string'
|
118
|
+
end
|
80
119
|
end
|
81
120
|
end
|
82
121
|
|
@@ -94,7 +133,7 @@ describe Elasticsearch::Transport::Transport::Base do
|
|
94
133
|
end
|
95
134
|
|
96
135
|
it 'raises an exception' do
|
97
|
-
expect { client.
|
136
|
+
expect { client.perform_request('GET', '/') }.to raise_exception(Faraday::ConnectionFailed)
|
98
137
|
end
|
99
138
|
end
|
100
139
|
|
@@ -106,7 +145,8 @@ describe Elasticsearch::Transport::Transport::Base do
|
|
106
145
|
let(:arguments) do
|
107
146
|
{
|
108
147
|
hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
|
109
|
-
retry_on_failure: 2
|
148
|
+
retry_on_failure: 2,
|
149
|
+
adapter: :net_http
|
110
150
|
}
|
111
151
|
end
|
112
152
|
|
@@ -129,8 +169,9 @@ describe Elasticsearch::Transport::Transport::Base do
|
|
129
169
|
|
130
170
|
let(:arguments) do
|
131
171
|
{
|
132
|
-
hosts:
|
133
|
-
retry_on_status: ['404']
|
172
|
+
hosts: ELASTICSEARCH_HOSTS,
|
173
|
+
retry_on_status: ['404'],
|
174
|
+
adapter: :net_http
|
134
175
|
}
|
135
176
|
end
|
136
177
|
|