elastic-transport 8.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/check_license_headers.rb +33 -0
  3. data/.github/license-header.txt +16 -0
  4. data/.github/workflows/license.yml +13 -0
  5. data/.github/workflows/tests.yml +45 -0
  6. data/.gitignore +19 -0
  7. data/CHANGELOG.md +224 -0
  8. data/Gemfile +38 -0
  9. data/LICENSE +202 -0
  10. data/README.md +552 -0
  11. data/Rakefile +87 -0
  12. data/elastic-transport.gemspec +74 -0
  13. data/lib/elastic/transport/client.rb +276 -0
  14. data/lib/elastic/transport/meta_header.rb +135 -0
  15. data/lib/elastic/transport/redacted.rb +73 -0
  16. data/lib/elastic/transport/transport/base.rb +450 -0
  17. data/lib/elastic/transport/transport/connections/collection.rb +126 -0
  18. data/lib/elastic/transport/transport/connections/connection.rb +160 -0
  19. data/lib/elastic/transport/transport/connections/selector.rb +91 -0
  20. data/lib/elastic/transport/transport/errors.rb +91 -0
  21. data/lib/elastic/transport/transport/http/curb.rb +120 -0
  22. data/lib/elastic/transport/transport/http/faraday.rb +95 -0
  23. data/lib/elastic/transport/transport/http/manticore.rb +179 -0
  24. data/lib/elastic/transport/transport/loggable.rb +83 -0
  25. data/lib/elastic/transport/transport/response.rb +36 -0
  26. data/lib/elastic/transport/transport/serializer/multi_json.rb +52 -0
  27. data/lib/elastic/transport/transport/sniffer.rb +101 -0
  28. data/lib/elastic/transport/version.rb +22 -0
  29. data/lib/elastic/transport.rb +37 -0
  30. data/lib/elastic-transport.rb +18 -0
  31. data/spec/elasticsearch/connections/collection_spec.rb +266 -0
  32. data/spec/elasticsearch/connections/selector_spec.rb +166 -0
  33. data/spec/elasticsearch/transport/base_spec.rb +264 -0
  34. data/spec/elasticsearch/transport/client_spec.rb +1651 -0
  35. data/spec/elasticsearch/transport/meta_header_spec.rb +274 -0
  36. data/spec/elasticsearch/transport/sniffer_spec.rb +275 -0
  37. data/spec/spec_helper.rb +90 -0
  38. data/test/integration/transport_test.rb +98 -0
  39. data/test/profile/client_benchmark_test.rb +132 -0
  40. data/test/test_helper.rb +83 -0
  41. data/test/unit/connection_test.rb +135 -0
  42. data/test/unit/response_test.rb +30 -0
  43. data/test/unit/serializer_test.rb +33 -0
  44. data/test/unit/transport_base_test.rb +664 -0
  45. data/test/unit/transport_curb_test.rb +135 -0
  46. data/test/unit/transport_faraday_test.rb +228 -0
  47. data/test/unit/transport_manticore_test.rb +251 -0
  48. metadata +412 -0
@@ -0,0 +1,166 @@
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 'spec_helper'
19
+
20
+ describe Elastic::Transport::Transport::Connections::Selector do
21
+ before do
22
+ class BackupStrategySelector
23
+ include Elastic::Transport::Transport::Connections::Selector::Base
24
+
25
+ def select(options={})
26
+ connections.reject do |c|
27
+ c.host[:attributes] && c.host[:attributes][:backup]
28
+ end.sample
29
+ end
30
+ end
31
+ end
32
+
33
+ after do
34
+ Object.send(:remove_const, :BackupStrategySelector)
35
+ end
36
+
37
+ let(:backup_strategy_selector) do
38
+ BackupStrategySelector.new
39
+ end
40
+
41
+ describe 'the Random selector' do
42
+ let(:connections) do
43
+ [1, 2]
44
+ end
45
+
46
+ let(:selector) do
47
+ described_class::Random.new(connections: connections)
48
+ end
49
+
50
+ it 'is initialized with connections' do
51
+ expect(selector.connections).to eq(connections)
52
+ end
53
+
54
+ describe '#select' do
55
+ let(:connections) do
56
+ (0..19).to_a
57
+ end
58
+
59
+ it 'returns a connection' do
60
+ expect(selector.select).to be_a(Integer)
61
+ end
62
+
63
+ context 'when multiple threads are used' do
64
+ it 'allows threads to select connections in parallel' do
65
+ expect(10.times.collect do
66
+ threads = []
67
+ 20.times do
68
+ threads << Thread.new do
69
+ selector.select
70
+ end
71
+ end
72
+ threads.map { |t| t.join }
73
+ selector.select
74
+ end).to all(be_a(Integer))
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ describe 'the RoundRobin selector' do
81
+ let(:connections) do
82
+ ['A', 'B', 'C']
83
+ end
84
+
85
+ let(:selector) do
86
+ described_class::RoundRobin.new(connections: connections)
87
+ end
88
+
89
+ it 'is initialized with connections' do
90
+ expect(selector.connections).to eq(connections)
91
+ end
92
+
93
+ describe '#select' do
94
+ it 'rotates over the connections' do
95
+ expect(selector.select).to eq('A')
96
+ expect(selector.select).to eq('B')
97
+ expect(selector.select).to eq('C')
98
+ expect(selector.select).to eq('A')
99
+ end
100
+
101
+ context 'when multiple threads are used' do
102
+ let(:connections) do
103
+ (0..19).to_a
104
+ end
105
+
106
+ it 'returns a connection' do
107
+ expect(selector.select).to be_a(Integer)
108
+ end
109
+
110
+ it 'allows threads to select connections in parallel' do
111
+ expect(10.times.collect do
112
+ threads = []
113
+ 20.times do
114
+ threads << Thread.new do
115
+ selector.select
116
+ end
117
+ end
118
+ threads.map { |t| t.join }
119
+ selector.select
120
+ end).to eq((0..9).to_a)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ describe 'a custom selector' do
127
+ let(:connections) do
128
+ [
129
+ double(host: { hostname: 'host1' }),
130
+ double(host: { hostname: 'host2', attributes: { backup: true } })
131
+ ]
132
+ end
133
+
134
+ let(:selector) do
135
+ BackupStrategySelector.new(connections: connections)
136
+ end
137
+
138
+ it 'is initialized with connections' do
139
+ expect(selector.connections).to eq(connections)
140
+ end
141
+
142
+ describe '#select' do
143
+ it 'applies the custom strategy' do
144
+ 10.times { expect(selector.select.host[:hostname]).to eq('host1') }
145
+ end
146
+ end
147
+ end
148
+
149
+ context 'when the Base module is included in a class' do
150
+ before do
151
+ class ExampleSelector
152
+ include Elastic::Transport::Transport::Connections::Selector::Base
153
+ end
154
+ end
155
+
156
+ after do
157
+ Object.send(:remove_const, :ExampleSelector)
158
+ end
159
+
160
+ it 'requires the #select method to be redefined' do
161
+ expect do
162
+ ExampleSelector.new.select
163
+ end.to raise_exception(NoMethodError)
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,264 @@
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 'spec_helper'
19
+
20
+ describe Elastic::Transport::Transport::Base do
21
+ context 'when a host is printed in a logged message' do
22
+ shared_examples_for 'a redacted string' do
23
+ let(:client) do
24
+ Elastic::Transport::Client.new(arguments)
25
+ end
26
+
27
+ let(:logger) do
28
+ double('logger', error?: true, error: '')
29
+ end
30
+
31
+ it 'does not include the password in the logged string' do
32
+ expect(logger).not_to receive(:error).with(/secret_password/)
33
+ expect {
34
+ client.perform_request('GET', '/_cluster/stats')
35
+ }.to raise_exception(Faraday::ConnectionFailed)
36
+ end
37
+
38
+ it 'replaces the password with the string \'REDACTED\'' do
39
+ expect(logger).to receive(:error).with(/REDACTED/)
40
+ expect {
41
+ client.perform_request('GET', '/_cluster/stats')
42
+ }.to raise_exception(Faraday::ConnectionFailed)
43
+ end
44
+ end
45
+
46
+ context 'when the user and password are provided as separate arguments' do
47
+ let(:arguments) do
48
+ {
49
+ hosts: 'fake',
50
+ logger: logger,
51
+ password: 'secret_password',
52
+ user: 'test'
53
+ }
54
+ end
55
+
56
+ it_behaves_like 'a redacted string'
57
+ end
58
+
59
+ context 'when the user and password are provided in the string URI' do
60
+ let(:arguments) do
61
+ {
62
+ hosts: 'http://test:secret_password@fake',
63
+ logger: logger
64
+ }
65
+ end
66
+
67
+ it_behaves_like 'a redacted string'
68
+ end
69
+
70
+ context 'when the user and password are provided in the URI object' do
71
+ let(:arguments) do
72
+ {
73
+ hosts: URI.parse('http://test:secret_password@fake'),
74
+ logger: logger
75
+ }
76
+ end
77
+
78
+ it_behaves_like 'a redacted string'
79
+ end
80
+ end
81
+
82
+ context 'when reload_on_failure is true and and hosts are unreachable' do
83
+ let(:client) do
84
+ Elastic::Transport::Client.new(arguments)
85
+ end
86
+
87
+ let(:arguments) do
88
+ {
89
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
90
+ reload_on_failure: true,
91
+ sniffer_timeout: 5
92
+ }
93
+ end
94
+
95
+ it 'raises an exception' do
96
+ expect { client.perform_request('GET', '/info') }.to raise_exception(Faraday::ConnectionFailed)
97
+ end
98
+ end
99
+
100
+ context 'when the client has `retry_on_failure` set to an integer' do
101
+ let(:client) do
102
+ Elastic::Transport::Client.new(arguments)
103
+ end
104
+
105
+ let(:arguments) do
106
+ {
107
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
108
+ retry_on_failure: 2
109
+ }
110
+ end
111
+
112
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
113
+ before do
114
+ expect(client.transport).to receive(:get_connection).exactly(3).times.and_call_original
115
+ end
116
+
117
+ it 'uses the client `retry_on_failure` value' do
118
+ expect {
119
+ client.transport.perform_request('GET', '/info')
120
+ }.to raise_exception(Faraday::ConnectionFailed)
121
+ end
122
+ end
123
+
124
+ context 'when `perform_request` is called with a `retry_on_status` option value' do
125
+ before do
126
+ expect(client.transport).to receive(:__raise_transport_error).exactly(6).times.and_call_original
127
+ end
128
+
129
+ let(:arguments) do
130
+ {
131
+ hosts: ELASTICSEARCH_HOSTS,
132
+ retry_on_status: ['404']
133
+ }
134
+ end
135
+
136
+ it 'retries on 404 status the specified number of max_retries' do
137
+ expect do
138
+ client.transport.perform_request('GET', 'myindex/_doc/1?routing=FOOBARBAZ', {}, nil, nil, retry_on_failure: 5)
139
+ end.to raise_exception(Elastic::Transport::Transport::Errors::NotFound)
140
+ end
141
+ end
142
+
143
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
144
+ before do
145
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
146
+ end
147
+
148
+ it 'uses the option `retry_on_failure` value' do
149
+ expect do
150
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
151
+ end.to raise_exception(Faraday::ConnectionFailed)
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'when the client has `retry_on_failure` set to true' do
157
+ let(:client) do
158
+ Elastic::Transport::Client.new(arguments)
159
+ end
160
+
161
+ let(:arguments) do
162
+ {
163
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
164
+ retry_on_failure: true
165
+ }
166
+ end
167
+
168
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
169
+ before do
170
+ expect(client.transport).to receive(:get_connection).exactly(4).times.and_call_original
171
+ end
172
+
173
+ it 'uses the default `MAX_RETRIES` value' do
174
+ expect {
175
+ client.transport.perform_request('GET', '/info')
176
+ }.to raise_exception(Faraday::ConnectionFailed)
177
+ end
178
+ end
179
+
180
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
181
+ before do
182
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
183
+ end
184
+
185
+ it 'uses the option `retry_on_failure` value' do
186
+ expect {
187
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
188
+ }.to raise_exception(Faraday::ConnectionFailed)
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'when the client has `retry_on_failure` set to false' do
194
+ let(:client) do
195
+ Elastic::Transport::Client.new(arguments)
196
+ end
197
+
198
+ let(:arguments) do
199
+ {
200
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
201
+ retry_on_failure: false
202
+ }
203
+ end
204
+
205
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
206
+ before do
207
+ expect(client.transport).to receive(:get_connection).once.and_call_original
208
+ end
209
+
210
+ it 'does not retry' do
211
+ expect {
212
+ client.transport.perform_request('GET', '/info')
213
+ }.to raise_exception(Faraday::ConnectionFailed)
214
+ end
215
+ end
216
+
217
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
218
+
219
+ before do
220
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
221
+ end
222
+
223
+ it 'uses the option `retry_on_failure` value' do
224
+ expect {
225
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
226
+ }.to raise_exception(Faraday::ConnectionFailed)
227
+ end
228
+ end
229
+ end
230
+
231
+ context 'when the client has no `retry_on_failure` set' do
232
+ let(:client) do
233
+ Elastic::Transport::Client.new(arguments)
234
+ end
235
+
236
+ let(:arguments) do
237
+ { hosts: ['http://unavailable:9200', 'http://unavailable:9201'] }
238
+ end
239
+
240
+ context 'when `perform_request` is called without a `retry_on_failure` option value' do
241
+ before do
242
+ expect(client.transport).to receive(:get_connection).exactly(1).times.and_call_original
243
+ end
244
+
245
+ it 'does not retry' do
246
+ expect do
247
+ client.transport.perform_request('GET', '/info')
248
+ end.to raise_exception(Faraday::ConnectionFailed)
249
+ end
250
+ end
251
+
252
+ context 'when `perform_request` is called with a `retry_on_failure` option value' do
253
+ before do
254
+ expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
255
+ end
256
+
257
+ it 'uses the option `retry_on_failure` value' do
258
+ expect do
259
+ client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
260
+ end.to raise_exception(Faraday::ConnectionFailed)
261
+ end
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,1651 @@
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 'spec_helper'
19
+
20
+ describe Elastic::Transport::Client do
21
+ let(:client) do
22
+ described_class.new.tap do |_client|
23
+ allow(_client).to receive(:__build_connections)
24
+ end
25
+ end
26
+
27
+ it 'has a default transport' do
28
+ expect(client.transport).to be_a(Elastic::Transport::Client::DEFAULT_TRANSPORT_CLASS)
29
+ end
30
+
31
+ it 'preserves the Faraday default user agent header' do
32
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/Faraday/)
33
+ end
34
+
35
+ it 'identifies the Ruby client in the User-Agent header' do
36
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/elastic-transport-ruby\/#{Elastic::Transport::VERSION}/)
37
+ end
38
+
39
+ it 'identifies the Ruby version in the User-Agent header' do
40
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RUBY_VERSION}/)
41
+ end
42
+
43
+ it 'identifies the host_os in the User-Agent header' do
44
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase}/)
45
+ end
46
+
47
+ it 'identifies the target_cpu in the User-Agent header' do
48
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['target_cpu']}/)
49
+ end
50
+
51
+ it 'sets the \'Content-Type\' header to \'application/json\' by default' do
52
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json')
53
+ end
54
+
55
+ it 'uses localhost by default' do
56
+ expect(client.transport.hosts[0][:host]).to eq('localhost')
57
+ end
58
+
59
+ context 'when a User-Agent header is specified as client option' do
60
+ let(:client) do
61
+ described_class.new(transport_options: { headers: { 'User-Agent' => 'testing' } })
62
+ end
63
+
64
+ it 'sets the specified User-Agent header' do
65
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing')
66
+ end
67
+ end
68
+
69
+ context 'when a user-agent header is specified as client option in lower-case' do
70
+
71
+ let(:client) do
72
+ described_class.new(transport_options: { headers: { 'user-agent' => 'testing' } })
73
+ end
74
+
75
+ it 'sets the specified User-Agent header' do
76
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing')
77
+ end
78
+ end
79
+
80
+ context 'when a Content-Type header is specified as client option' do
81
+
82
+ let(:client) do
83
+ described_class.new(transport_options: { headers: { 'Content-Type' => 'testing' } })
84
+ end
85
+
86
+ it 'sets the specified Content-Type header' do
87
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing')
88
+ end
89
+ end
90
+
91
+ context 'when a content-type header is specified as client option in lower-case' do
92
+
93
+ let(:client) do
94
+ described_class.new(transport_options: { headers: { 'content-type' => 'testing' } })
95
+ end
96
+
97
+ it 'sets the specified Content-Type header' do
98
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing')
99
+ end
100
+ end
101
+
102
+ context 'when the Curb transport class is used', unless: jruby? do
103
+
104
+ let(:client) do
105
+ described_class.new(transport_class: Elastic::Transport::Transport::HTTP::Curb)
106
+ end
107
+
108
+ it 'preserves the Curb default user agent header' do
109
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/Curb/)
110
+ end
111
+
112
+ it 'identifies the Ruby client in the User-Agent header' do
113
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/elastic-transport-ruby\/#{Elastic::Transport::VERSION}/)
114
+ end
115
+
116
+ it 'identifies the Ruby version in the User-Agent header' do
117
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RUBY_VERSION}/)
118
+ end
119
+
120
+ it 'identifies the host_os in the User-Agent header' do
121
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase}/)
122
+ end
123
+
124
+ it 'identifies the target_cpu in the User-Agent header' do
125
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['target_cpu']}/)
126
+ end
127
+
128
+ it 'sets the \'Content-Type\' header to \'application/json\' by default' do
129
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json')
130
+ end
131
+
132
+ it 'uses localhost by default' do
133
+ expect(client.transport.hosts[0][:host]).to eq('localhost')
134
+ end
135
+
136
+ context 'when a User-Agent header is specified as a client option' do
137
+
138
+ let(:client) do
139
+ described_class.new(transport_class: Elastic::Transport::Transport::HTTP::Curb,
140
+ transport_options: { headers: { 'User-Agent' => 'testing' } })
141
+ end
142
+
143
+ it 'sets the specified User-Agent header' do
144
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing')
145
+ end
146
+ end
147
+
148
+ context 'when a user-agent header is specified as a client option as lower-case' do
149
+
150
+ let(:client) do
151
+ described_class.new(transport_class: Elastic::Transport::Transport::HTTP::Curb,
152
+ transport_options: { headers: { 'user-agent' => 'testing' } })
153
+ end
154
+
155
+ it 'sets the specified User-Agent header' do
156
+ expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing')
157
+ end
158
+ end
159
+
160
+ context 'when a Content-Type header is specified as client option' do
161
+
162
+ let(:client) do
163
+ described_class.new(transport_class: Elastic::Transport::Transport::HTTP::Curb,
164
+ transport_options: { headers: { 'Content-Type' => 'testing' } })
165
+ end
166
+
167
+ it 'sets the specified Content-Type header' do
168
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing')
169
+ end
170
+ end
171
+
172
+ context 'when a content-type header is specified as client option in lower-case' do
173
+
174
+ let(:client) do
175
+ described_class.new(transport_class: Elastic::Transport::Transport::HTTP::Curb,
176
+ transport_options: { headers: { 'content-type' => 'testing' } })
177
+ end
178
+
179
+ it 'sets the specified Content-Type header' do
180
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing')
181
+ end
182
+ end
183
+ end
184
+
185
+ describe 'adapter' do
186
+ context 'when no adapter is specified' do
187
+ fork do
188
+ let(:client) { described_class.new }
189
+ let(:adapter) { client.transport.connections.all.first.connection.builder.adapter }
190
+
191
+ it 'uses Faraday NetHttp' do
192
+ expect(adapter).to eq Faraday::Adapter::NetHttp
193
+ end
194
+ end unless jruby?
195
+ end
196
+
197
+ context 'when the adapter is patron' do
198
+ let(:adapter) do
199
+ client.transport.connections.all.first.connection.builder.adapter
200
+ end
201
+
202
+ let(:client) do
203
+ described_class.new(adapter: :patron, enable_meta_header: false)
204
+ end
205
+
206
+ it 'uses Faraday with the adapter' do
207
+ expect(adapter).to eq Faraday::Adapter::Patron
208
+ end
209
+ end
210
+
211
+ context 'when the adapter is typhoeus' do
212
+ let(:adapter) do
213
+ client.transport.connections.all.first.connection.builder.adapter
214
+ end
215
+
216
+ let(:client) do
217
+ described_class.new(adapter: :typhoeus, enable_meta_header: false)
218
+ end
219
+
220
+ it 'uses Faraday with the adapter' do
221
+ expect(adapter).to eq Faraday::Adapter::Typhoeus
222
+ end
223
+ end unless jruby?
224
+
225
+ context 'when the adapter is specified as a string key' do
226
+ let(:adapter) do
227
+ client.transport.connections.all.first.connection.builder.adapter
228
+ end
229
+
230
+ let(:client) do
231
+ described_class.new(adapter: :patron, enable_meta_header: false)
232
+ end
233
+
234
+ it 'uses Faraday with the adapter' do
235
+ expect(adapter).to eq Faraday::Adapter::Patron
236
+ end
237
+ end
238
+
239
+ context 'when the adapter can be detected', unless: jruby? do
240
+ around do |example|
241
+ require 'patron'; load 'patron.rb'
242
+ example.run
243
+ end
244
+
245
+ let(:adapter) do
246
+ client.transport.connections.all.first.connection.builder.adapter
247
+ end
248
+
249
+ it 'uses the detected adapter' do
250
+ expect(adapter).to eq Faraday::Adapter::Patron
251
+ end
252
+ end
253
+
254
+ context 'when the Faraday adapter is configured' do
255
+ let(:client) do
256
+ described_class.new do |faraday|
257
+ faraday.adapter :patron
258
+ faraday.response :logger
259
+ end
260
+ end
261
+
262
+ let(:adapter) do
263
+ client.transport.connections.all.first.connection.builder.adapter
264
+ end
265
+
266
+ let(:handlers) do
267
+ client.transport.connections.all.first.connection.builder.handlers
268
+ end
269
+
270
+ it 'sets the adapter' do
271
+ expect(adapter).to eq Faraday::Adapter::Patron
272
+ end
273
+
274
+ it 'sets the logger' do
275
+ expect(handlers).to include(Faraday::Response::Logger)
276
+ end
277
+ end
278
+ end
279
+
280
+ shared_examples_for 'a client that extracts hosts' do
281
+ context 'when the host is a String' do
282
+ context 'when there is a protocol specified' do
283
+ context 'when credentials are specified \'http://USERNAME:PASSWORD@myhost:8080\'' do
284
+ let(:host) do
285
+ 'http://USERNAME:PASSWORD@myhost:8080'
286
+ end
287
+
288
+ it 'extracts the credentials' do
289
+ expect(hosts[0][:user]).to eq('USERNAME')
290
+ expect(hosts[0][:password]).to eq('PASSWORD')
291
+ end
292
+
293
+ it 'extracts the host' do
294
+ expect(hosts[0][:host]).to eq('myhost')
295
+ end
296
+
297
+ it 'extracts the port' do
298
+ expect(hosts[0][:port]).to be(8080)
299
+ end
300
+ end
301
+
302
+ context 'when there is a trailing slash \'http://myhost/\'' do
303
+ let(:host) do
304
+ 'http://myhost/'
305
+ end
306
+
307
+ it 'extracts the host' do
308
+ expect(hosts[0][:host]).to eq('myhost')
309
+ expect(hosts[0][:scheme]).to eq('http')
310
+ expect(hosts[0][:path]).to eq('')
311
+ end
312
+
313
+ it 'extracts the scheme' do
314
+ expect(hosts[0][:scheme]).to eq('http')
315
+ end
316
+
317
+ it 'extracts the path' do
318
+ expect(hosts[0][:path]).to eq('')
319
+ end
320
+ end
321
+
322
+ context 'when there is a trailing slash with a path \'http://myhost/foo/bar/\'' do
323
+ let(:host) do
324
+ 'http://myhost/foo/bar/'
325
+ end
326
+
327
+ it 'extracts the host' do
328
+ expect(hosts[0][:host]).to eq('myhost')
329
+ expect(hosts[0][:scheme]).to eq('http')
330
+ expect(hosts[0][:path]).to eq('/foo/bar')
331
+ end
332
+ end
333
+
334
+ context 'when the protocol is http' do
335
+ context 'when there is no port specified \'http://myhost\'' do
336
+ let(:host) do
337
+ 'http://myhost'
338
+ end
339
+
340
+ it 'extracts the host' do
341
+ expect(hosts[0][:host]).to eq('myhost')
342
+ end
343
+
344
+ it 'extracts the protocol' do
345
+ expect(hosts[0][:protocol]).to eq('http')
346
+ end
347
+
348
+ it 'defaults to port 9200' do
349
+ expect(hosts[0][:port]).to be(9200)
350
+ end
351
+ end
352
+
353
+ context 'when there is a port specified \'http://myhost:7101\'' do
354
+
355
+ let(:host) do
356
+ 'http://myhost:7101'
357
+ end
358
+
359
+ it 'extracts the host' do
360
+ expect(hosts[0][:host]).to eq('myhost')
361
+ end
362
+
363
+ it 'extracts the protocol' do
364
+ expect(hosts[0][:protocol]).to eq('http')
365
+ end
366
+
367
+ it 'extracts the port' do
368
+ expect(hosts[0][:port]).to be(7101)
369
+ end
370
+
371
+ context 'when there is a path specified \'http://myhost:7101/api\'' do
372
+
373
+ let(:host) do
374
+ 'http://myhost:7101/api'
375
+ end
376
+
377
+ it 'sets the path' do
378
+ expect(hosts[0][:host]).to eq('myhost')
379
+ expect(hosts[0][:protocol]).to eq('http')
380
+ expect(hosts[0][:path]).to eq('/api')
381
+ expect(hosts[0][:port]).to be(7101)
382
+ end
383
+
384
+ it 'extracts the host' do
385
+ expect(hosts[0][:host]).to eq('myhost')
386
+ end
387
+
388
+ it 'extracts the protocol' do
389
+ expect(hosts[0][:protocol]).to eq('http')
390
+ end
391
+
392
+ it 'extracts the port' do
393
+ expect(hosts[0][:port]).to be(7101)
394
+ end
395
+
396
+ it 'extracts the path' do
397
+ expect(hosts[0][:path]).to eq('/api')
398
+ end
399
+ end
400
+ end
401
+ end
402
+
403
+ context 'when the protocol is https' do
404
+
405
+ context 'when there is no port specified \'https://myhost\'' do
406
+
407
+ let(:host) do
408
+ 'https://myhost'
409
+ end
410
+
411
+ it 'extracts the host' do
412
+ expect(hosts[0][:host]).to eq('myhost')
413
+ end
414
+
415
+ it 'extracts the protocol' do
416
+ expect(hosts[0][:protocol]).to eq('https')
417
+ end
418
+
419
+ it 'defaults to port 443' do
420
+ expect(hosts[0][:port]).to be(443)
421
+ end
422
+ end
423
+
424
+ context 'when there is a port specified \'https://myhost:7101\'' do
425
+
426
+ let(:host) do
427
+ 'https://myhost:7101'
428
+ end
429
+
430
+ it 'extracts the host' do
431
+ expect(hosts[0][:host]).to eq('myhost')
432
+ end
433
+
434
+ it 'extracts the protocol' do
435
+ expect(hosts[0][:protocol]).to eq('https')
436
+ end
437
+
438
+ it 'extracts the port' do
439
+ expect(hosts[0][:port]).to be(7101)
440
+ end
441
+
442
+ context 'when there is a path specified \'https://myhost:7101/api\'' do
443
+
444
+ let(:host) do
445
+ 'https://myhost:7101/api'
446
+ end
447
+
448
+ it 'extracts the host' do
449
+ expect(hosts[0][:host]).to eq('myhost')
450
+ end
451
+
452
+ it 'extracts the protocol' do
453
+ expect(hosts[0][:protocol]).to eq('https')
454
+ end
455
+
456
+ it 'extracts the port' do
457
+ expect(hosts[0][:port]).to be(7101)
458
+ end
459
+
460
+ it 'extracts the path' do
461
+ expect(hosts[0][:path]).to eq('/api')
462
+ end
463
+ end
464
+ end
465
+
466
+ context 'when IPv6 format is used' do
467
+
468
+ around do |example|
469
+ original_setting = Faraday.ignore_env_proxy
470
+ Faraday.ignore_env_proxy = true
471
+ example.run
472
+ Faraday.ignore_env_proxy = original_setting
473
+ end
474
+
475
+ let(:host) do
476
+ 'https://[2090:db8:85a3:9811::1f]:8080'
477
+ end
478
+
479
+ it 'extracts the host' do
480
+ expect(hosts[0][:host]).to eq('[2090:db8:85a3:9811::1f]')
481
+ end
482
+
483
+ it 'extracts the protocol' do
484
+ expect(hosts[0][:protocol]).to eq('https')
485
+ end
486
+
487
+ it 'extracts the port' do
488
+ expect(hosts[0][:port]).to be(8080)
489
+ end
490
+
491
+ it 'creates the correct full url' do
492
+ expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://[2090:db8:85a3:9811::1f]:8080')
493
+ end
494
+ end
495
+ end
496
+ end
497
+
498
+ context 'when no protocol is specified \'myhost\'' do
499
+
500
+ let(:host) do
501
+ 'myhost'
502
+ end
503
+
504
+ it 'defaults to http' do
505
+ expect(hosts[0][:host]).to eq('myhost')
506
+ expect(hosts[0][:protocol]).to eq('http')
507
+ end
508
+
509
+ it 'uses port 9200' do
510
+ expect(hosts[0][:port]).to be(9200)
511
+ end
512
+ end
513
+ end
514
+
515
+ context 'when the host is a Hash' do
516
+
517
+ let(:host) do
518
+ { :host => 'myhost', :scheme => 'https' }
519
+ end
520
+
521
+ it 'extracts the host' do
522
+ expect(hosts[0][:host]).to eq('myhost')
523
+ end
524
+
525
+ it 'extracts the protocol' do
526
+ expect(hosts[0][:protocol]).to eq('https')
527
+ end
528
+
529
+ it 'extracts the port' do
530
+ expect(hosts[0][:port]).to be(9200)
531
+ end
532
+
533
+ context 'when IPv6 format is used' do
534
+
535
+ around do |example|
536
+ original_setting = Faraday.ignore_env_proxy
537
+ Faraday.ignore_env_proxy = true
538
+ example.run
539
+ Faraday.ignore_env_proxy = original_setting
540
+ end
541
+
542
+ let(:host) do
543
+ { host: '[2090:db8:85a3:9811::1f]', scheme: 'https', port: '443' }
544
+ end
545
+
546
+ it 'extracts the host' do
547
+ expect(hosts[0][:host]).to eq('[2090:db8:85a3:9811::1f]')
548
+ expect(hosts[0][:scheme]).to eq('https')
549
+ expect(hosts[0][:port]).to be(443)
550
+ end
551
+
552
+ it 'creates the correct full url' do
553
+ expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://[2090:db8:85a3:9811::1f]:443')
554
+ end
555
+ end
556
+
557
+ context 'when the host is localhost as a IPv6 address' do
558
+
559
+ around do |example|
560
+ original_setting = Faraday.ignore_env_proxy
561
+ Faraday.ignore_env_proxy = true
562
+ example.run
563
+ Faraday.ignore_env_proxy = original_setting
564
+ end
565
+
566
+ let(:host) do
567
+ { host: '[::1]' }
568
+ end
569
+
570
+ it 'extracts the host' do
571
+ expect(hosts[0][:host]).to eq('[::1]')
572
+ expect(hosts[0][:port]).to be(9200)
573
+ end
574
+
575
+ it 'creates the correct full url' do
576
+ expect(client.transport.__full_url(client.transport.hosts[0])).to eq('http://[::1]:9200')
577
+ end
578
+ end
579
+
580
+ context 'when the port is specified as a String' do
581
+
582
+ let(:host) do
583
+ { host: 'myhost', scheme: 'https', port: '443' }
584
+ end
585
+
586
+ it 'extracts the host' do
587
+ expect(hosts[0][:host]).to eq('myhost')
588
+ end
589
+
590
+ it 'extracts the protocol' do
591
+ expect(hosts[0][:scheme]).to eq('https')
592
+ end
593
+
594
+ it 'converts the port to an integer' do
595
+ expect(hosts[0][:port]).to be(443)
596
+ end
597
+ end
598
+
599
+ context 'when the port is specified as an Integer' do
600
+
601
+ let(:host) do
602
+ { host: 'myhost', scheme: 'https', port: 443 }
603
+ end
604
+
605
+ it 'extracts the host' do
606
+ expect(hosts[0][:host]).to eq('myhost')
607
+ end
608
+
609
+ it 'extracts the protocol' do
610
+ expect(hosts[0][:scheme]).to eq('https')
611
+ end
612
+
613
+ it 'extracts port as an integer' do
614
+ expect(hosts[0][:port]).to be(443)
615
+ end
616
+ end
617
+ end
618
+
619
+ context 'when the hosts are a Hashie:Mash' do
620
+
621
+ let(:host) do
622
+ Hashie::Mash.new(host: 'myhost', scheme: 'https')
623
+ end
624
+
625
+ it 'extracts the host' do
626
+ expect(hosts[0][:host]).to eq('myhost')
627
+ end
628
+
629
+ it 'extracts the protocol' do
630
+ expect(hosts[0][:scheme]).to eq('https')
631
+ end
632
+
633
+ it 'converts the port to an integer' do
634
+ expect(hosts[0][:port]).to be(9200)
635
+ end
636
+
637
+ context 'when the port is specified as a String' do
638
+
639
+ let(:host) do
640
+ Hashie::Mash.new(host: 'myhost', scheme: 'https', port: '443')
641
+ end
642
+
643
+ it 'extracts the host' do
644
+ expect(hosts[0][:host]).to eq('myhost')
645
+ end
646
+
647
+ it 'extracts the protocol' do
648
+ expect(hosts[0][:scheme]).to eq('https')
649
+ end
650
+
651
+ it 'converts the port to an integer' do
652
+ expect(hosts[0][:port]).to be(443)
653
+ end
654
+ end
655
+
656
+ context 'when the port is specified as an Integer' do
657
+
658
+ let(:host) do
659
+ Hashie::Mash.new(host: 'myhost', scheme: 'https', port: 443)
660
+ end
661
+
662
+ it 'extracts the host' do
663
+ expect(hosts[0][:host]).to eq('myhost')
664
+ end
665
+
666
+ it 'extracts the protocol' do
667
+ expect(hosts[0][:scheme]).to eq('https')
668
+ end
669
+
670
+ it 'extracts port as an integer' do
671
+ expect(hosts[0][:port]).to be(443)
672
+ end
673
+ end
674
+ end
675
+
676
+ context 'when the hosts are an array' do
677
+
678
+ context 'when there is one host' do
679
+
680
+ let(:host) do
681
+ ['myhost']
682
+ end
683
+
684
+ it 'extracts the host' do
685
+ expect(hosts[0][:host]).to eq('myhost')
686
+ end
687
+
688
+ it 'extracts the protocol' do
689
+ expect(hosts[0][:protocol]).to eq('http')
690
+ end
691
+
692
+ it 'defaults to port 9200' do
693
+ expect(hosts[0][:port]).to be(9200)
694
+ end
695
+ end
696
+
697
+ context 'when there is one host with a protocol and no port' do
698
+
699
+ let(:host) do
700
+ ['http://myhost']
701
+ end
702
+
703
+ it 'extracts the host' do
704
+ expect(hosts[0][:host]).to eq('myhost')
705
+ end
706
+
707
+ it 'extracts the protocol' do
708
+ expect(hosts[0][:scheme]).to eq('http')
709
+ end
710
+
711
+ it 'defaults to port 9200' do
712
+ expect(hosts[0][:port]).to be(9200)
713
+ end
714
+ end
715
+
716
+ context 'when there is one host with a protocol and the default http port explicitly provided' do
717
+ let(:host) do
718
+ ['http://myhost:80']
719
+ end
720
+
721
+ it 'respects the explicit port' do
722
+ expect(hosts[0][:port]).to be(80)
723
+ end
724
+ end
725
+
726
+ context 'when there is one host with a protocol and the default https port explicitly provided' do
727
+ let(:host) do
728
+ ['https://myhost:443']
729
+ end
730
+
731
+ it 'respects the explicit port' do
732
+ expect(hosts[0][:port]).to be(443)
733
+ end
734
+ end
735
+
736
+ context 'when there is one host with a protocol and no port' do
737
+ let(:host) do
738
+ ['https://myhost']
739
+ end
740
+
741
+ it 'extracts the host' do
742
+ expect(hosts[0][:host]).to eq('myhost')
743
+ end
744
+
745
+ it 'extracts the protocol' do
746
+ expect(hosts[0][:scheme]).to eq('https')
747
+ end
748
+
749
+ it 'defaults to port 443' do
750
+ expect(hosts[0][:port]).to be(443)
751
+ end
752
+ end
753
+
754
+ context 'when there is one host with a protocol, path, and no port' do
755
+ let(:host) do
756
+ ['http://myhost/foo/bar']
757
+ end
758
+
759
+ it 'extracts the host' do
760
+ expect(hosts[0][:host]).to eq('myhost')
761
+ end
762
+
763
+ it 'extracts the protocol' do
764
+ expect(hosts[0][:scheme]).to eq('http')
765
+ end
766
+
767
+ it 'defaults to port 9200' do
768
+ expect(hosts[0][:port]).to be(9200)
769
+ end
770
+
771
+ it 'extracts the path' do
772
+ expect(hosts[0][:path]).to eq('/foo/bar')
773
+ end
774
+ end
775
+
776
+ context 'when there is more than one host' do
777
+ let(:host) do
778
+ ['host1', 'host2']
779
+ end
780
+
781
+ it 'extracts the hosts' do
782
+ expect(hosts[0][:host]).to eq('host1')
783
+ expect(hosts[0][:protocol]).to eq('http')
784
+ expect(hosts[0][:port]).to be(9200)
785
+ expect(hosts[1][:host]).to eq('host2')
786
+ expect(hosts[1][:protocol]).to eq('http')
787
+ expect(hosts[1][:port]).to be(9200)
788
+ end
789
+ end
790
+
791
+ context 'when ports are also specified' do
792
+ let(:host) do
793
+ ['host1:1000', 'host2:2000']
794
+ end
795
+
796
+ it 'extracts the hosts' do
797
+ expect(hosts[0][:host]).to eq('host1')
798
+ expect(hosts[0][:protocol]).to eq('http')
799
+ expect(hosts[0][:port]).to be(1000)
800
+ expect(hosts[1][:host]).to eq('host2')
801
+ expect(hosts[1][:protocol]).to eq('http')
802
+ expect(hosts[1][:port]).to be(2000)
803
+ end
804
+ end
805
+ end
806
+
807
+ context 'when the hosts is an instance of URI' do
808
+ let(:host) do
809
+ URI.parse('https://USERNAME:PASSWORD@myhost:4430')
810
+ end
811
+
812
+ it 'extracts the host' do
813
+ expect(hosts[0][:host]).to eq('myhost')
814
+ expect(hosts[0][:scheme]).to eq('https')
815
+ expect(hosts[0][:port]).to be(4430)
816
+ expect(hosts[0][:user]).to eq('USERNAME')
817
+ expect(hosts[0][:password]).to eq('PASSWORD')
818
+ end
819
+ end
820
+
821
+ context 'when the hosts is invalid' do
822
+ let(:host) do
823
+ 123
824
+ end
825
+
826
+ it 'extracts the host' do
827
+ expect {
828
+ hosts
829
+ }.to raise_exception(ArgumentError)
830
+ end
831
+ end
832
+ end
833
+
834
+ context 'when hosts are specified with the \'host\' key' do
835
+ let(:client) do
836
+ described_class.new(host: ['host1', 'host2', 'host3', 'host4'], randomize_hosts: true)
837
+ end
838
+
839
+ let(:hosts) do
840
+ client.transport.hosts
841
+ end
842
+
843
+ it 'sets the hosts in random order' do
844
+ expect(hosts.all? { |host| client.transport.hosts.include?(host) }).to be(true)
845
+ end
846
+ end
847
+
848
+ context 'when hosts are specified with the \'host\' key as a String' do
849
+ let(:client) do
850
+ described_class.new('host' => ['host1', 'host2', 'host3', 'host4'], 'randomize_hosts' => true)
851
+ end
852
+
853
+ let(:hosts) do
854
+ client.transport.hosts
855
+ end
856
+
857
+ it 'sets the hosts in random order' do
858
+ expect(hosts.all? { |host| client.transport.hosts.include?(host) }).to be(true)
859
+ end
860
+ end
861
+
862
+ context 'when hosts are specified with the \'hosts\' key' do
863
+ let(:client) do
864
+ described_class.new(hosts: host)
865
+ end
866
+
867
+ let(:hosts) do
868
+ client.transport.hosts
869
+ end
870
+
871
+ it_behaves_like 'a client that extracts hosts'
872
+ end
873
+
874
+ context 'when hosts are specified with the \'hosts\' key as a String' do
875
+ let(:client) do
876
+ described_class.new('hosts' => host)
877
+ end
878
+
879
+ let(:hosts) do
880
+ client.transport.hosts
881
+ end
882
+
883
+ it_behaves_like 'a client that extracts hosts'
884
+ end
885
+
886
+ context 'when hosts are specified with the \'url\' key' do
887
+ let(:client) do
888
+ described_class.new(url: host)
889
+ end
890
+
891
+ let(:hosts) do
892
+ client.transport.hosts
893
+ end
894
+
895
+ it_behaves_like 'a client that extracts hosts'
896
+ end
897
+
898
+ context 'when hosts are specified with the \'url\' key as a String' do
899
+ let(:client) do
900
+ described_class.new('url' => host)
901
+ end
902
+
903
+ let(:hosts) do
904
+ client.transport.hosts
905
+ end
906
+
907
+ it_behaves_like 'a client that extracts hosts'
908
+ end
909
+
910
+ context 'when hosts are specified with the \'urls\' key' do
911
+ let(:client) do
912
+ described_class.new(urls: host)
913
+ end
914
+
915
+ let(:hosts) do
916
+ client.transport.hosts
917
+ end
918
+
919
+ it_behaves_like 'a client that extracts hosts'
920
+ end
921
+
922
+ context 'when hosts are specified with the \'urls\' key as a String' do
923
+ let(:client) do
924
+ described_class.new('urls' => host)
925
+ end
926
+
927
+ let(:hosts) do
928
+ client.transport.hosts
929
+ end
930
+
931
+ it_behaves_like 'a client that extracts hosts'
932
+ end
933
+
934
+ context 'when the URL is set in the ELASTICSEARCH_URL environment variable' do
935
+ context 'when there is only one host specified' do
936
+ around do |example|
937
+ before_url = ENV['ELASTICSEARCH_URL']
938
+ ENV['ELASTICSEARCH_URL'] = 'example.com'
939
+ example.run
940
+ ENV['ELASTICSEARCH_URL'] = before_url
941
+ end
942
+
943
+ it 'sets the host' do
944
+ expect(client.transport.hosts[0][:host]).to eq('example.com')
945
+ expect(client.transport.hosts.size).to eq(1)
946
+ end
947
+ end
948
+
949
+ context 'when mutliple hosts are specified as a comma-separated String list' do
950
+ around do |example|
951
+ before_url = ENV['ELASTICSEARCH_URL']
952
+ ENV['ELASTICSEARCH_URL'] = 'example.com, other.com'
953
+ example.run
954
+ ENV['ELASTICSEARCH_URL'] = before_url
955
+ end
956
+
957
+ it 'sets the hosts' do
958
+ expect(client.transport.hosts[0][:host]).to eq('example.com')
959
+ expect(client.transport.hosts[1][:host]).to eq('other.com')
960
+ expect(client.transport.hosts.size).to eq(2)
961
+ end
962
+ end
963
+ end
964
+
965
+ context 'when options are defined' do
966
+
967
+ context 'when scheme is specified' do
968
+
969
+ let(:client) do
970
+ described_class.new(scheme: 'https')
971
+ end
972
+
973
+ it 'sets the scheme' do
974
+ expect(client.transport.connections[0].full_url('')).to match(/https/)
975
+ end
976
+ end
977
+
978
+ context 'when scheme is specified as a String key' do
979
+
980
+ let(:client) do
981
+ described_class.new('scheme' => 'https')
982
+ end
983
+
984
+ it 'sets the scheme' do
985
+ expect(client.transport.connections[0].full_url('')).to match(/https/)
986
+ end
987
+ end
988
+
989
+ context 'when user and password are specified' do
990
+
991
+ let(:client) do
992
+ described_class.new(user: 'USERNAME', password: 'PASSWORD')
993
+ end
994
+
995
+ it 'sets the user and password' do
996
+ expect(client.transport.connections[0].full_url('')).to match(/USERNAME/)
997
+ expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/)
998
+ end
999
+
1000
+ context 'when the connections are reloaded' do
1001
+
1002
+ before do
1003
+ allow(client.transport.sniffer).to receive(:hosts).and_return([{ host: 'foobar', port: 4567, id: 'foobar4567' }])
1004
+ client.transport.reload_connections!
1005
+ end
1006
+
1007
+ it 'sets keeps user and password' do
1008
+ expect(client.transport.connections[0].full_url('')).to match(/USERNAME/)
1009
+ expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/)
1010
+ expect(client.transport.connections[0].full_url('')).to match(/foobar/)
1011
+ end
1012
+ end
1013
+ end
1014
+
1015
+ context 'when user and password are specified as String keys' do
1016
+
1017
+ let(:client) do
1018
+ described_class.new('user' => 'USERNAME', 'password' => 'PASSWORD')
1019
+ end
1020
+
1021
+ it 'sets the user and password' do
1022
+ expect(client.transport.connections[0].full_url('')).to match(/USERNAME/)
1023
+ expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/)
1024
+ end
1025
+
1026
+ context 'when the connections are reloaded' do
1027
+
1028
+ before do
1029
+ allow(client.transport.sniffer).to receive(:hosts).and_return([{ host: 'foobar', port: 4567, id: 'foobar4567' }])
1030
+ client.transport.reload_connections!
1031
+ end
1032
+
1033
+ it 'sets keeps user and password' do
1034
+ expect(client.transport.connections[0].full_url('')).to match(/USERNAME/)
1035
+ expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/)
1036
+ expect(client.transport.connections[0].full_url('')).to match(/foobar/)
1037
+ end
1038
+ end
1039
+ end
1040
+
1041
+ context 'when port is specified' do
1042
+
1043
+ let(:client) do
1044
+ described_class.new(host: 'node1', port: 1234)
1045
+ end
1046
+
1047
+ it 'sets the port' do
1048
+ expect(client.transport.connections[0].full_url('')).to match(/1234/)
1049
+ end
1050
+ end
1051
+
1052
+ context 'when the log option is true' do
1053
+
1054
+ let(:client) do
1055
+ described_class.new(log: true)
1056
+ end
1057
+
1058
+ it 'has a default logger for transport' do
1059
+ expect(client.transport.logger.info).to eq(described_class::DEFAULT_LOGGER.call.info)
1060
+ end
1061
+ end
1062
+
1063
+ context 'when the trace option is true' do
1064
+
1065
+ let(:client) do
1066
+ described_class.new(trace: true)
1067
+ end
1068
+
1069
+ it 'has a default logger for transport' do
1070
+ expect(client.transport.tracer.info).to eq(described_class::DEFAULT_TRACER.call.info)
1071
+ end
1072
+ end
1073
+
1074
+ context 'when a custom transport class is specified' do
1075
+
1076
+ let(:transport_class) do
1077
+ Class.new { def initialize(*); end }
1078
+ end
1079
+
1080
+ let(:client) do
1081
+ described_class.new(transport_class: transport_class)
1082
+ end
1083
+
1084
+ it 'allows the custom transport class to be defined' do
1085
+ expect(client.transport).to be_a(transport_class)
1086
+ end
1087
+ end
1088
+
1089
+ context 'when a custom transport instance is specified' do
1090
+
1091
+ let(:transport_instance) do
1092
+ Class.new { def initialize(*); end }.new
1093
+ end
1094
+
1095
+ let(:client) do
1096
+ described_class.new(transport: transport_instance)
1097
+ end
1098
+
1099
+ it 'allows the custom transport class to be defined' do
1100
+ expect(client.transport).to be(transport_instance)
1101
+ end
1102
+ end
1103
+
1104
+ context 'when \'transport_options\' are defined' do
1105
+
1106
+ let(:client) do
1107
+ described_class.new(transport_options: { request: { timeout: 1 } })
1108
+ end
1109
+
1110
+ it 'sets the options on the transport' do
1111
+ expect(client.transport.options[:transport_options][:request]).to eq(timeout: 1)
1112
+ end
1113
+ end
1114
+
1115
+ context 'when \'request_timeout\' is defined' do
1116
+
1117
+ let(:client) do
1118
+ described_class.new(request_timeout: 120)
1119
+ end
1120
+
1121
+ it 'sets the options on the transport' do
1122
+ expect(client.transport.options[:transport_options][:request]).to eq(timeout: 120)
1123
+ end
1124
+ end
1125
+
1126
+ context 'when \'request_timeout\' is defined as a String key' do
1127
+
1128
+ let(:client) do
1129
+ described_class.new('request_timeout' => 120)
1130
+ end
1131
+
1132
+ it 'sets the options on the transport' do
1133
+ expect(client.transport.options[:transport_options][:request]).to eq(timeout: 120)
1134
+ end
1135
+ end
1136
+ end
1137
+
1138
+ describe '#perform_request' do
1139
+
1140
+ let(:transport_instance) do
1141
+ Class.new { def initialize(*); end }.new
1142
+ end
1143
+
1144
+ let(:client) do
1145
+ described_class.new(transport: transport_instance)
1146
+ end
1147
+
1148
+ it 'delegates performing requests to the transport' do
1149
+ expect(transport_instance).to receive(:perform_request).and_return(true)
1150
+ expect(client.perform_request('GET', '/')).to be(true)
1151
+ end
1152
+
1153
+ context 'when the \'send_get_body_as\' option is specified' do
1154
+
1155
+ let(:client) do
1156
+ described_class.new(transport: transport_instance, :send_get_body_as => 'POST')
1157
+ end
1158
+
1159
+ before do
1160
+ expect(transport_instance).to receive(:perform_request).with('POST', '/', {},
1161
+ '{"foo":"bar"}',
1162
+ '{"Content-Type":"application/x-ndjson"}').and_return(true)
1163
+ end
1164
+
1165
+ let(:request) do
1166
+ client.perform_request('POST', '/', {}, '{"foo":"bar"}', '{"Content-Type":"application/x-ndjson"}')
1167
+ end
1168
+
1169
+ it 'sets the option' do
1170
+ expect(request).to be(true)
1171
+ end
1172
+ end
1173
+
1174
+ context 'when Elasticsearch response includes a warning header' do
1175
+ let(:client) do
1176
+ Elastic::Transport::Client.new(hosts: hosts)
1177
+ end
1178
+
1179
+ let(:warning) { 'Elasticsearch warning: "deprecation warning"' }
1180
+
1181
+ it 'prints a warning' do
1182
+ allow_any_instance_of(Elastic::Transport::Transport::Response).to receive(:headers) do
1183
+ { 'warning' => warning }
1184
+ end
1185
+
1186
+ begin
1187
+ stderr = $stderr
1188
+ fake_stderr = StringIO.new
1189
+ $stderr = fake_stderr
1190
+
1191
+ client.perform_request('GET', '/')
1192
+ fake_stderr.rewind
1193
+ expect(fake_stderr.string).to eq("warning: #{warning}\n")
1194
+ ensure
1195
+ $stderr = stderr
1196
+ end
1197
+ end
1198
+ end
1199
+ end
1200
+
1201
+ context 'when the client connects to Elasticsearch' do
1202
+ let(:logger) do
1203
+ Logger.new(STDERR).tap do |logger|
1204
+ logger.formatter = proc do |severity, datetime, progname, msg|
1205
+ color = case severity
1206
+ when /INFO/ then :green
1207
+ when /ERROR|WARN|FATAL/ then :red
1208
+ when /DEBUG/ then :cyan
1209
+ else :white
1210
+ end
1211
+ ANSI.ansi(severity[0] + ' ', color, :faint) + ANSI.ansi(msg, :white, :faint) + "\n"
1212
+ end
1213
+ end unless ENV['QUIET']
1214
+ end
1215
+
1216
+ let(:port) do
1217
+ TEST_PORT
1218
+ end
1219
+
1220
+ let(:transport_options) do
1221
+ {}
1222
+ end
1223
+
1224
+ let(:options) do
1225
+ {}
1226
+ end
1227
+
1228
+ let(:client) do
1229
+ described_class.new({ host: hosts, logger: logger }.merge!(transport_options: transport_options).merge!(options))
1230
+ end
1231
+
1232
+ context 'when a request is made' do
1233
+ let!(:response) do
1234
+ client.perform_request('GET', '_cluster/health')
1235
+ end
1236
+
1237
+ it 'connects to the cluster' do
1238
+ expect(response.body['number_of_nodes']).to be >= (1)
1239
+ end
1240
+ end
1241
+
1242
+ describe '#initialize' do
1243
+ context 'when options are specified' do
1244
+ let(:transport_options) do
1245
+ { headers: { accept: 'application/yaml', content_type: 'application/yaml' } }
1246
+ end
1247
+
1248
+ let(:response) do
1249
+ client.perform_request('GET', '_cluster/health')
1250
+ end
1251
+
1252
+ it 'applies the options to the client' do
1253
+ expect(response.body).to match(/---\n/)
1254
+ expect(response.headers['content-type']).to eq('application/yaml')
1255
+ end
1256
+ end
1257
+
1258
+ context 'when a block is provided' do
1259
+ let(:client) do
1260
+ Elastic::Transport::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client|
1261
+ client.headers['Accept'] = 'application/yaml'
1262
+ end
1263
+ end
1264
+
1265
+ let(:response) do
1266
+ client.perform_request('GET', '_cluster/health')
1267
+ end
1268
+
1269
+ it 'executes the block' do
1270
+ expect(response.body).to match(/---\n/)
1271
+ expect(response.headers['content-type']).to eq('application/yaml')
1272
+ end
1273
+
1274
+ context 'when the Faraday adapter is set in the block' do
1275
+ let(:client) do
1276
+ Elastic::Transport::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client|
1277
+ client.adapter(:net_http_persistent)
1278
+ end
1279
+ end
1280
+
1281
+ let(:handler_name) do
1282
+ client.transport.connections.first.connection.builder.adapter.name
1283
+ end
1284
+
1285
+ let(:response) do
1286
+ client.perform_request('GET', '_cluster/health')
1287
+ end
1288
+
1289
+ it 'sets the adapter' do
1290
+ expect(handler_name).to eq('Faraday::Adapter::NetHttpPersistent')
1291
+ end
1292
+
1293
+ it 'uses the adapter to connect' do
1294
+ expect(response.status).to eq(200)
1295
+ end
1296
+ end
1297
+ end
1298
+ end
1299
+
1300
+ describe '#options' do
1301
+ context 'when retry_on_failure is true' do
1302
+ context 'when a node is unreachable' do
1303
+ let(:hosts) do
1304
+ [ELASTICSEARCH_HOSTS.first, "foobar1", "foobar2"]
1305
+ end
1306
+
1307
+ let(:options) do
1308
+ { retry_on_failure: true }
1309
+ end
1310
+
1311
+ let(:responses) do
1312
+ 5.times.collect do
1313
+ client.perform_request('GET', '_nodes/_local')
1314
+ end
1315
+ end
1316
+
1317
+ it 'retries on failure' do
1318
+ expect(responses.all? { true }).to be(true)
1319
+ end
1320
+ end
1321
+ end
1322
+
1323
+ context 'when retry_on_failure is an integer' do
1324
+ let(:hosts) do
1325
+ [ELASTICSEARCH_HOSTS.first, 'foobar1', 'foobar2', 'foobar3']
1326
+ end
1327
+
1328
+ let(:options) do
1329
+ { retry_on_failure: 1 }
1330
+ end
1331
+
1332
+ it 'retries only the specified number of times' do
1333
+ expect(client.perform_request('GET', '_nodes/_local'))
1334
+ expect {
1335
+ client.perform_request('GET', '_nodes/_local')
1336
+ }.to raise_exception(Faraday::ConnectionFailed)
1337
+ end
1338
+ end
1339
+
1340
+ context 'when reload_on_failure is true' do
1341
+ let(:hosts) do
1342
+ [ELASTICSEARCH_HOSTS.first, 'foobar1', 'foobar2']
1343
+ end
1344
+
1345
+ let(:options) do
1346
+ { reload_on_failure: true }
1347
+ end
1348
+
1349
+ let(:responses) do
1350
+ 5.times.collect do
1351
+ client.perform_request('GET', '_nodes/_local')
1352
+ end
1353
+ end
1354
+
1355
+ it 'reloads the connections' do
1356
+ expect(client.transport.connections.size).to eq(3)
1357
+ expect(responses.all? { true }).to be(true)
1358
+ expect(client.transport.connections.size).to be >= (1)
1359
+ end
1360
+ end
1361
+
1362
+ context 'when retry_on_status is specified' do
1363
+ let(:options) do
1364
+ { retry_on_status: 400 }
1365
+ end
1366
+
1367
+ let(:logger) do
1368
+ double('logger', :debug? => false, :warn? => true, :fatal? => false, :error? => false)
1369
+ end
1370
+
1371
+ before do
1372
+ expect(logger).to receive(:warn).exactly(4).times
1373
+ end
1374
+
1375
+ it 'retries when the status matches' do
1376
+ expect {
1377
+ client.perform_request('PUT', '_foobar')
1378
+ }.to raise_exception(Elastic::Transport::Transport::Errors::BadRequest)
1379
+ end
1380
+ end
1381
+
1382
+ context 'when the \'compression\' option is set to true' do
1383
+ context 'when using Faraday as the transport' do
1384
+ context 'when using the Net::HTTP adapter' do
1385
+ let(:client) do
1386
+ described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :net_http)
1387
+ end
1388
+
1389
+ it 'compresses the request and decompresses the response' do
1390
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1391
+ end
1392
+
1393
+ it 'sets the Accept-Encoding header' do
1394
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1395
+ end
1396
+
1397
+ it 'preserves the other headers' do
1398
+ expect(client.transport.connections[0].connection.headers['User-Agent'])
1399
+ end
1400
+ end
1401
+
1402
+ context 'when using the HTTPClient adapter' do
1403
+ let(:client) do
1404
+ described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :httpclient, enable_meta_header: false)
1405
+ end
1406
+
1407
+ it 'compresses the request and decompresses the response' do
1408
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1409
+ end
1410
+
1411
+ it 'sets the Accept-Encoding header' do
1412
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1413
+ end
1414
+
1415
+ it 'preserves the other headers' do
1416
+ expect(client.transport.connections[0].connection.headers['User-Agent'])
1417
+ end
1418
+ end
1419
+
1420
+ context 'when using the Patron adapter', unless: jruby? do
1421
+ let(:client) do
1422
+ described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :patron)
1423
+ end
1424
+
1425
+ it 'compresses the request and decompresses the response' do
1426
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1427
+ end
1428
+
1429
+ it 'sets the Accept-Encoding header' do
1430
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1431
+ end
1432
+
1433
+ it 'preserves the other headers' do
1434
+ expect(client.transport.connections[0].connection.headers['User-Agent'])
1435
+ end
1436
+ end
1437
+
1438
+ context 'when using the Net::HTTP::Persistent adapter' do
1439
+ let(:client) do
1440
+ described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :net_http_persistent)
1441
+ end
1442
+
1443
+ it 'compresses the request and decompresses the response' do
1444
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1445
+ end
1446
+
1447
+ it 'sets the Accept-Encoding header' do
1448
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1449
+ end
1450
+
1451
+ it 'preserves the other headers' do
1452
+ expect(client.transport.connections[0].connection.headers['User-Agent'])
1453
+ end
1454
+ end
1455
+
1456
+ context 'when using the Typhoeus adapter' do
1457
+ let(:client) do
1458
+ described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :typhoeus)
1459
+ end
1460
+
1461
+ it 'compresses the request and decompresses the response' do
1462
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1463
+ end
1464
+
1465
+ it 'sets the Accept-Encoding header' do
1466
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1467
+ end
1468
+
1469
+ it 'preserves the other headers' do
1470
+ expect(client.transport.connections[0].connection.headers['User-Agent'])
1471
+ end
1472
+ end unless jruby?
1473
+ end
1474
+ end
1475
+
1476
+ context 'when using Curb as the transport', unless: jruby? do
1477
+ let(:client) do
1478
+ described_class.new(hosts: ELASTICSEARCH_HOSTS,
1479
+ compression: true,
1480
+ transport_class: Elastic::Transport::Transport::HTTP::Curb)
1481
+ end
1482
+
1483
+ it 'compresses the request and decompresses the response' do
1484
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1485
+ end
1486
+
1487
+ it 'sets the Accept-Encoding header' do
1488
+ expect(client.transport.connections[0].connection.headers['Accept-Encoding'])
1489
+ end
1490
+
1491
+ it 'preserves the other headers' do
1492
+ expect(client.transport.connections[0].connection.headers['User-Agent'])
1493
+ end
1494
+ end
1495
+
1496
+ context 'when using Manticore as the transport', if: jruby? do
1497
+ let(:client) do
1498
+ described_class.new(hosts: ELASTICSEARCH_HOSTS,
1499
+ compression: true,
1500
+ transport_class: Elastic::Transport::Transport::HTTP::Manticore)
1501
+ end
1502
+
1503
+ it 'compresses the request and decompresses the response' do
1504
+ expect(client.perform_request('GET', '/').body).to be_a(Hash)
1505
+ end
1506
+ end
1507
+ end
1508
+
1509
+ describe '#perform_request' do
1510
+ context 'when a request is made' do
1511
+ before do
1512
+ client.perform_request('DELETE', '_all')
1513
+ client.perform_request('DELETE', 'myindex') rescue
1514
+ client.perform_request('PUT', 'myindex', {}, { settings: { number_of_shards: 2, number_of_replicas: 0 } })
1515
+ client.perform_request('PUT', 'myindex/_doc/1', { routing: 'XYZ', timeout: '1s' }, { foo: 'bar' })
1516
+ client.perform_request('GET', '_cluster/health?wait_for_status=green&timeout=2s', {})
1517
+ end
1518
+
1519
+ let(:response) do
1520
+ client.perform_request('GET', 'myindex/_doc/1?routing=XYZ')
1521
+ end
1522
+
1523
+ it 'handles paths and URL paramters' do
1524
+ expect(response.status).to eq(200)
1525
+ end
1526
+
1527
+ it 'returns response body' do
1528
+ expect(response.body['_source']).to eq('foo' => 'bar')
1529
+ end
1530
+ end
1531
+
1532
+ context 'when an invalid url is specified' do
1533
+ it 'raises an exception' do
1534
+ expect {
1535
+ client.perform_request('GET', 'myindex/_doc/1?routing=FOOBARBAZ')
1536
+ }.to raise_exception(Elastic::Transport::Transport::Errors::NotFound)
1537
+ end
1538
+ end
1539
+
1540
+ context 'when the \'ignore\' parameter is specified' do
1541
+
1542
+ let(:response) do
1543
+ client.perform_request('PUT', '_foobar', ignore: 400)
1544
+ end
1545
+
1546
+ it 'exposes the status in the response' do
1547
+ expect(response.status).to eq(400)
1548
+ end
1549
+
1550
+ it 'exposes the body of the response' do
1551
+ expect(response.body).to be_a(Hash)
1552
+ expect(response.body.inspect).to match(/invalid_index_name_exception/)
1553
+ end
1554
+ end
1555
+
1556
+ context 'when request headers are specified' do
1557
+
1558
+ let(:response) do
1559
+ client.perform_request('GET', '/', {}, nil, { 'Content-Type' => 'application/yaml' })
1560
+ end
1561
+
1562
+ it 'passes them to the transport' do
1563
+ expect(response.body).to match(/---/)
1564
+ end
1565
+ end
1566
+
1567
+ describe 'selector' do
1568
+
1569
+ context 'when the round-robin selector is used' do
1570
+
1571
+ let(:nodes) do
1572
+ 3.times.collect do
1573
+ client.perform_request('GET', '_nodes/_local').body['nodes'].to_a[0][1]['name']
1574
+ end
1575
+ end
1576
+
1577
+ let(:node_names) do
1578
+ client.nodes.stats['nodes'].collect do |name, stats|
1579
+ stats['name']
1580
+ end
1581
+ end
1582
+
1583
+ let(:expected_names) do
1584
+ 3.times.collect do |i|
1585
+ node_names[i % node_names.size]
1586
+ end
1587
+ end
1588
+
1589
+ # it 'rotates nodes' do
1590
+ # pending 'Better way to detect rotating nodes'
1591
+ # expect(nodes).to eq(expected_names)
1592
+ # end
1593
+ end
1594
+ end
1595
+
1596
+ context 'when patron is used as an adapter', unless: jruby? do
1597
+ before do
1598
+ require 'patron'
1599
+ end
1600
+
1601
+ let(:options) do
1602
+ { adapter: :patron }
1603
+ end
1604
+
1605
+ let(:adapter) do
1606
+ client.transport.connections.first.connection.builder.adapter
1607
+ end
1608
+
1609
+ it 'uses the patron connection handler' do
1610
+ expect(adapter).to eq('Faraday::Adapter::Patron')
1611
+ end
1612
+
1613
+ it 'keeps connections open' do
1614
+ response = client.perform_request('GET', '_nodes/stats/http')
1615
+ connections_before = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1616
+ client.transport.reload_connections!
1617
+ response = client.perform_request('GET', '_nodes/stats/http')
1618
+ connections_after = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1619
+ expect(connections_after).to be >= (connections_before)
1620
+ end
1621
+ end
1622
+
1623
+ context 'when typhoeus is used as an adapter', unless: jruby? do
1624
+ before do
1625
+ require 'typhoeus'
1626
+ end
1627
+
1628
+ let(:options) do
1629
+ { adapter: :typhoeus }
1630
+ end
1631
+
1632
+ let(:adapter) do
1633
+ client.transport.connections.first.connection.builder.adapter
1634
+ end
1635
+
1636
+ it 'uses the patron connection handler' do
1637
+ expect(adapter).to eq('Faraday::Adapter::Typhoeus')
1638
+ end
1639
+
1640
+ it 'keeps connections open' do
1641
+ response = client.perform_request('GET', '_nodes/stats/http')
1642
+ connections_before = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1643
+ client.transport.reload_connections!
1644
+ response = client.perform_request('GET', '_nodes/stats/http')
1645
+ connections_after = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1646
+ expect(connections_after).to be >= (connections_before)
1647
+ end
1648
+ end
1649
+ end
1650
+ end
1651
+ end