elasticsearch-transport 6.3.0 → 6.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +86 -32
  3. data/elasticsearch-transport.gemspec +45 -64
  4. data/lib/elasticsearch-transport.rb +4 -0
  5. data/lib/elasticsearch/transport.rb +4 -0
  6. data/lib/elasticsearch/transport/client.rb +141 -20
  7. data/lib/elasticsearch/transport/redacted.rb +4 -0
  8. data/lib/elasticsearch/transport/transport/base.rb +17 -7
  9. data/lib/elasticsearch/transport/transport/connections/collection.rb +4 -0
  10. data/lib/elasticsearch/transport/transport/connections/connection.rb +4 -0
  11. data/lib/elasticsearch/transport/transport/connections/selector.rb +4 -0
  12. data/lib/elasticsearch/transport/transport/errors.rb +4 -0
  13. data/lib/elasticsearch/transport/transport/http/curb.rb +6 -2
  14. data/lib/elasticsearch/transport/transport/http/faraday.rb +6 -2
  15. data/lib/elasticsearch/transport/transport/http/manticore.rb +5 -1
  16. data/lib/elasticsearch/transport/transport/response.rb +4 -0
  17. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +4 -0
  18. data/lib/elasticsearch/transport/transport/sniffer.rb +31 -3
  19. data/lib/elasticsearch/transport/version.rb +5 -1
  20. data/spec/elasticsearch/transport/base_spec.rb +187 -8
  21. data/spec/elasticsearch/transport/client_spec.rb +157 -23
  22. data/spec/elasticsearch/transport/meta_header_spec.rb +214 -0
  23. data/spec/elasticsearch/transport/sniffer_spec.rb +269 -0
  24. data/spec/spec_helper.rb +11 -0
  25. data/test/integration/transport_test.rb +4 -0
  26. data/test/profile/client_benchmark_test.rb +4 -0
  27. data/test/test_helper.rb +4 -0
  28. data/test/unit/connection_collection_test.rb +4 -0
  29. data/test/unit/connection_selector_test.rb +4 -0
  30. data/test/unit/connection_test.rb +4 -0
  31. data/test/unit/response_test.rb +5 -1
  32. data/test/unit/serializer_test.rb +4 -0
  33. data/test/unit/transport_base_test.rb +4 -0
  34. data/test/unit/transport_curb_test.rb +4 -0
  35. data/test/unit/transport_faraday_test.rb +4 -0
  36. data/test/unit/transport_manticore_test.rb +4 -0
  37. metadata +80 -54
  38. data/test/unit/sniffer_test.rb +0 -179
@@ -1,3 +1,7 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+
1
5
  # Licensed to Elasticsearch B.V. under one or more contributor
2
6
  # license agreements. See the NOTICE file distributed with
3
7
  # this work for additional information regarding copyright
@@ -45,31 +49,99 @@ describe Elasticsearch::Transport::Client do
45
49
  expect(client.transport.hosts[0][:host]).to eq('localhost')
46
50
  end
47
51
 
48
- describe 'adapter' do
52
+ context 'when an encoded api_key is provided' do
53
+ let(:client) do
54
+ described_class.new(api_key: 'an_api_key')
55
+ end
56
+ let(:authorization_header) do
57
+ client.transport.connections.first.connection.headers['Authorization']
58
+ end
49
59
 
50
- context 'when no adapter is specified' do
60
+ it 'Adds the ApiKey header to the connection' do
61
+ expect(authorization_header).to eq('ApiKey an_api_key')
62
+ end
63
+ end
64
+
65
+ context 'when an un-encoded api_key is provided' do
66
+ let(:client) do
67
+ described_class.new(api_key: { id: 'my_id', api_key: 'my_api_key' })
68
+ end
69
+ let(:authorization_header) do
70
+ client.transport.connections.first.connection.headers['Authorization']
71
+ end
72
+
73
+ it 'Adds the ApiKey header to the connection' do
74
+ expect(authorization_header).to eq("ApiKey #{Base64.strict_encode64('my_id:my_api_key')}")
75
+ end
76
+ end
77
+
78
+ context 'when basic auth and api_key are provided' do
79
+ let(:client) do
80
+ described_class.new(
81
+ api_key: { id: 'my_id', api_key: 'my_api_key' },
82
+ host: 'http://elastic:password@localhost:9200'
83
+ )
84
+ end
85
+ let(:authorization_header) do
86
+ client.transport.connections.first.connection.headers['Authorization']
87
+ end
88
+
89
+ it 'removes basic auth credentials' do
90
+ expect(authorization_header).not_to match(/^Basic/)
91
+ expect(authorization_header).to match(/^ApiKey/)
92
+ end
93
+ end
51
94
 
95
+ describe 'adapter' do
96
+ context 'when no adapter is specified' do
52
97
  let(:adapter) do
53
- client.transport.connections.all.first.connection.builder.handlers
98
+ client.transport.connections.all.first.connection.builder.adapter
54
99
  end
55
100
 
56
101
  it 'uses Faraday NetHttp' do
57
- expect(adapter).to include(Faraday::Adapter::NetHttp)
102
+ expect(adapter).to eq Faraday::Adapter::NetHttp
103
+ end
104
+ end
105
+
106
+ context 'when the adapter is patron' do
107
+ let(:adapter) do
108
+ client.transport.connections.all.first.connection.builder.adapter
109
+ end
110
+
111
+ let(:client) do
112
+ described_class.new(adapter: :patron, enable_meta_header: false)
113
+ end
114
+
115
+ it 'uses Faraday with the adapter' do
116
+ expect(adapter).to eq Faraday::Adapter::Patron
58
117
  end
59
118
  end
60
119
 
61
- context 'when the adapter is specified' do
120
+ context 'when the adapter is typhoeus' do
121
+ let(:adapter) do
122
+ client.transport.connections.all.first.connection.builder.adapter
123
+ end
124
+
125
+ let(:client) do
126
+ described_class.new(adapter: :typhoeus, enable_meta_header: false)
127
+ end
62
128
 
129
+ it 'uses Faraday with the adapter' do
130
+ expect(adapter).to eq Faraday::Adapter::Typhoeus
131
+ end
132
+ end
133
+
134
+ context 'when the adapter is specified as a string key' do
63
135
  let(:adapter) do
64
- client.transport.connections.all.first.connection.builder.handlers
136
+ client.transport.connections.all.first.connection.builder.adapter
65
137
  end
66
138
 
67
139
  let(:client) do
68
- described_class.new(adapter: :typhoeus)
140
+ described_class.new('adapter' => :patron, enable_meta_header: false)
69
141
  end
70
142
 
71
- it 'uses Faraday' do
72
- expect(adapter).to include(Faraday::Adapter::Typhoeus)
143
+ it 'uses Faraday with the adapter' do
144
+ expect(adapter).to eq Faraday::Adapter::Patron
73
145
  end
74
146
  end
75
147
 
@@ -81,11 +153,11 @@ describe Elasticsearch::Transport::Client do
81
153
  end
82
154
 
83
155
  let(:adapter) do
84
- client.transport.connections.all.first.connection.builder.handlers
156
+ client.transport.connections.all.first.connection.builder.adapter
85
157
  end
86
158
 
87
159
  it 'uses the detected adapter' do
88
- expect(adapter).to include(Faraday::Adapter::Patron)
160
+ expect(adapter).to eq Faraday::Adapter::Patron
89
161
  end
90
162
  end
91
163
 
@@ -93,17 +165,21 @@ describe Elasticsearch::Transport::Client do
93
165
 
94
166
  let(:client) do
95
167
  described_class.new do |faraday|
96
- faraday.adapter :typhoeus
168
+ faraday.adapter :patron
97
169
  faraday.response :logger
98
170
  end
99
171
  end
100
172
 
173
+ let(:adapter) do
174
+ client.transport.connections.all.first.connection.builder.adapter
175
+ end
176
+
101
177
  let(:handlers) do
102
178
  client.transport.connections.all.first.connection.builder.handlers
103
179
  end
104
180
 
105
181
  it 'sets the adapter' do
106
- expect(handlers).to include(Faraday::Adapter::Typhoeus)
182
+ expect(adapter).to eq Faraday::Adapter::Patron
107
183
  end
108
184
 
109
185
  it 'sets the logger' do
@@ -154,7 +230,6 @@ describe Elasticsearch::Transport::Client do
154
230
  end
155
231
 
156
232
  context 'when credentials are specified' do
157
-
158
233
  let(:host) do
159
234
  'http://USERNAME:PASSWORD@myhost:8080'
160
235
  end
@@ -611,10 +686,43 @@ describe Elasticsearch::Transport::Client do
611
686
  expect(request).to be(true)
612
687
  end
613
688
  end
689
+
690
+ context 'when x-opaque-id is set' do
691
+ let(:client) { described_class.new(host: hosts) }
692
+
693
+ it 'uses x-opaque-id on a request' do
694
+ expect(client.perform_request('GET', '/', { opaque_id: '12345' }).headers['x-opaque-id']).to eq('12345')
695
+ end
696
+ end
697
+
698
+ context 'when an x-opaque-id prefix is set on initialization' do
699
+ let(:prefix) { 'elastic_cloud' }
700
+ let(:client) do
701
+ described_class.new(host: hosts, opaque_id_prefix: prefix)
702
+ end
703
+
704
+ it 'uses x-opaque-id on a request' do
705
+ expect(client.perform_request('GET', '/', { opaque_id: '12345' }).headers['x-opaque-id']).to eq("#{prefix}12345")
706
+ end
707
+
708
+ context 'when using an API call' do
709
+ let(:client) { described_class.new(host: hosts) }
710
+
711
+ it 'doesnae raise an ArgumentError' do
712
+ expect { client.search(opaque_id: 'no_error') }.not_to raise_error
713
+ end
714
+
715
+ it 'uses X-Opaque-Id in the header' do
716
+ allow(client).to receive(:perform_request) { OpenStruct.new(body: '') }
717
+ expect { client.search(opaque_id: 'opaque_id') }.not_to raise_error
718
+ expect(client).to have_received(:perform_request)
719
+ .with('GET', '_search', { opaque_id: 'opaque_id' }, nil)
720
+ end
721
+ end
722
+ end
614
723
  end
615
724
 
616
725
  context 'when the client connects to Elasticsearch' do
617
-
618
726
  let(:logger) do
619
727
  Logger.new(STDERR).tap do |logger|
620
728
  logger.formatter = proc do |severity, datetime, progname, msg|
@@ -692,15 +800,14 @@ describe Elasticsearch::Transport::Client do
692
800
  end
693
801
 
694
802
  context 'when the Faraday adapter is set in the block' do
695
-
696
803
  let(:client) do
697
804
  Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client|
698
805
  client.adapter(:net_http_persistent)
699
806
  end
700
807
  end
701
808
 
702
- let(:connection_handler) do
703
- client.transport.connections.first.connection.builder.handlers.first
809
+ let(:handler_name) do
810
+ client.transport.connections.first.connection.builder.adapter.name
704
811
  end
705
812
 
706
813
  let(:response) do
@@ -708,7 +815,7 @@ describe Elasticsearch::Transport::Client do
708
815
  end
709
816
 
710
817
  it 'sets the adapter' do
711
- expect(connection_handler.name).to eq('Faraday::Adapter::NetHttpPersistent')
818
+ expect(handler_name).to eq('Faraday::Adapter::NetHttpPersistent')
712
819
  end
713
820
 
714
821
  it 'uses the adapter to connect' do
@@ -758,7 +865,7 @@ describe Elasticsearch::Transport::Client do
758
865
  expect(client.perform_request('GET', '_nodes/_local'))
759
866
  expect {
760
867
  client.perform_request('GET', '_nodes/_local')
761
- }.to raise_exception(Faraday::Error::ConnectionFailed)
868
+ }.to raise_exception(Faraday::ConnectionFailed)
762
869
  end
763
870
  end
764
871
 
@@ -907,12 +1014,39 @@ describe Elasticsearch::Transport::Client do
907
1014
  { adapter: :patron }
908
1015
  end
909
1016
 
910
- let(:connection_handler) do
911
- client.transport.connections.first.connection.builder.handlers.first
1017
+ let(:adapter) do
1018
+ client.transport.connections.first.connection.builder.adapter
1019
+ end
1020
+
1021
+ it 'uses the patron connection handler' do
1022
+ expect(adapter).to eq('Faraday::Adapter::Patron')
1023
+ end
1024
+
1025
+ it 'keeps connections open' do
1026
+ response = client.perform_request('GET', '_nodes/stats/http')
1027
+ connections_before = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1028
+ client.transport.reload_connections!
1029
+ response = client.perform_request('GET', '_nodes/stats/http')
1030
+ connections_after = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1031
+ expect(connections_after).to be >= (connections_before)
1032
+ end
1033
+ end
1034
+
1035
+ context 'when typhoeus is used as an adapter', unless: jruby? do
1036
+ before do
1037
+ require 'typhoeus'
1038
+ end
1039
+
1040
+ let(:options) do
1041
+ { adapter: :typhoeus }
1042
+ end
1043
+
1044
+ let(:adapter) do
1045
+ client.transport.connections.first.connection.builder.adapter
912
1046
  end
913
1047
 
914
1048
  it 'uses the patron connection handler' do
915
- expect(connection_handler).to eq('Faraday::Adapter::Patron')
1049
+ expect(adapter).to eq('Faraday::Adapter::Typhoeus')
916
1050
  end
917
1051
 
918
1052
  it 'keeps connections open' do
@@ -0,0 +1,214 @@
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 Elasticsearch::Transport::Client do
21
+ context 'meta-header' do
22
+ let(:subject) { client.transport.connections.first.connection.headers }
23
+ let(:client) { described_class.new }
24
+ let(:regexp) { /^[a-z]{1,}=[a-z0-9.\-]{1,}(?:,[a-z]{1,}=[a-z0-9._\-]+)*$/ }
25
+ let(:adapter) { :net_http }
26
+ let(:adapter_code) { "nh=#{defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}" }
27
+ let(:meta_header) do
28
+ if jruby?
29
+ "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION},#{adapter_code}"
30
+ else
31
+ "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},fd=#{Faraday::VERSION},#{adapter_code}"
32
+ end
33
+ end
34
+
35
+ context 'client_meta_version_' do
36
+ let(:version) { ['7.1.0-alpha', '7.11.0.pre.1', '8.0.0-beta', '8.0.0.beta.2']}
37
+
38
+ it 'converts the version to X.X.Xp' do
39
+ expect(client.send(:client_meta_version, '7.0.0-alpha')).to eq('7.0.0p')
40
+ expect(client.send(:client_meta_version, '7.11.0.pre.1')).to eq('7.11.0p')
41
+ expect(client.send(:client_meta_version, '8.1.0-beta')).to eq('8.1.0p')
42
+ expect(client.send(:client_meta_version, '8.0.0.beta.2')).to eq('8.0.0p')
43
+ expect(client.send(:client_meta_version, '12.16.4.pre')).to eq('12.16.4p')
44
+ end
45
+ end
46
+
47
+ # We are testing this method in the previous block, so now using it inside the test to make the
48
+ # Elasticsearch version in the meta header string dynamic
49
+ def meta_version
50
+ client.send(:client_meta_version, Elasticsearch::VERSION)
51
+ end
52
+
53
+ context 'single use of meta header' do
54
+ let(:client) do
55
+ described_class.new(adapter: adapter).tap do |klient|
56
+ allow(klient).to receive(:__build_connections)
57
+ end
58
+ end
59
+
60
+ it 'x-elastic-client-header value matches regexp' do
61
+ expect(subject['x-elastic-client-meta']).to match(regexp)
62
+ expect(subject).to include('x-elastic-client-meta' => meta_header)
63
+ end
64
+ end
65
+
66
+ context 'when using user-agent headers' do
67
+ let(:client) do
68
+ transport_options = { headers: { user_agent: 'My Ruby App' } }
69
+ described_class.new(transport_options: transport_options, adapter: adapter).tap do |klient|
70
+ allow(klient).to receive(:__build_connections)
71
+ end
72
+ end
73
+
74
+ it 'is friendly to previously set headers' do
75
+ expect(subject).to include(user_agent: 'My Ruby App')
76
+ expect(subject['x-elastic-client-meta']).to match(regexp)
77
+ expect(subject).to include('x-elastic-client-meta' => meta_header)
78
+ end
79
+ end
80
+
81
+ context 'when using API Key' do
82
+ let(:client) do
83
+ described_class.new(api_key: 'an_api_key', adapter: adapter)
84
+ end
85
+
86
+ let(:authorization_header) do
87
+ client.transport.connections.first.connection.headers['Authorization']
88
+ end
89
+
90
+ it 'adds the ApiKey header to the connection' do
91
+ expect(authorization_header).to eq('ApiKey an_api_key')
92
+ expect(subject['x-elastic-client-meta']).to match(regexp)
93
+ expect(subject).to include('x-elastic-client-meta' => meta_header)
94
+ end
95
+ end
96
+
97
+ context 'adapters' do
98
+ let(:meta_header) do
99
+ if jruby?
100
+ "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION}"
101
+ else
102
+ "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},fd=#{Faraday::VERSION}"
103
+ end
104
+ end
105
+ let(:client) { described_class.new(adapter: adapter) }
106
+ let(:headers) { client.transport.connections.first.connection.headers }
107
+
108
+ context 'using net/http/persistent' do
109
+ let(:adapter) { :net_http_persistent }
110
+
111
+ it 'sets adapter in the meta header' do
112
+ require 'net/http/persistent'
113
+ expect(headers['x-elastic-client-meta']).to match(regexp)
114
+ meta = "#{meta_header},np=#{Net::HTTP::Persistent::VERSION}"
115
+ expect(headers).to include('x-elastic-client-meta' => meta)
116
+ end
117
+ end
118
+
119
+ context 'using httpclient' do
120
+ let(:adapter) { :httpclient }
121
+
122
+ it 'sets adapter in the meta header' do
123
+ require 'httpclient'
124
+ expect(headers['x-elastic-client-meta']).to match(regexp)
125
+ meta = "#{meta_header},hc=#{HTTPClient::VERSION}"
126
+ expect(headers).to include('x-elastic-client-meta' => meta)
127
+ end
128
+ end
129
+
130
+ context 'using typhoeus' do
131
+ let(:adapter) { :typhoeus }
132
+
133
+ it 'sets adapter in the meta header' do
134
+ require 'typhoeus'
135
+ expect(headers['x-elastic-client-meta']).to match(regexp)
136
+ meta = "#{meta_header},ty=#{Typhoeus::VERSION}"
137
+ expect(headers).to include('x-elastic-client-meta' => meta)
138
+ end
139
+ end
140
+
141
+ unless defined?(JRUBY_VERSION)
142
+ let(:adapter) { :patron }
143
+
144
+ context 'using patron' do
145
+ it 'sets adapter in the meta header' do
146
+ require 'patron'
147
+ expect(headers['x-elastic-client-meta']).to match(regexp)
148
+ meta = "#{meta_header},pt=#{Patron::VERSION}"
149
+ expect(headers).to include('x-elastic-client-meta' => meta)
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ if defined?(JRUBY_VERSION)
156
+ context 'when using manticore' do
157
+ let(:client) do
158
+ Elasticsearch::Client.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore)
159
+ end
160
+ let(:subject) { client.transport.connections.first.connection.instance_variable_get("@options")[:headers]}
161
+
162
+ it 'sets manticore in the metaheader' do
163
+ expect(subject['x-elastic-client-meta']).to match(regexp)
164
+ expect(subject['x-elastic-client-meta']).to match(/mc=[0-9.]+/)
165
+ end
166
+ end
167
+ else
168
+ context 'when using curb' do
169
+ let(:client) do
170
+ Elasticsearch::Client.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb)
171
+ end
172
+
173
+ it 'sets curb in the metaheader' do
174
+ expect(subject['x-elastic-client-meta']).to match(regexp)
175
+ expect(subject['x-elastic-client-meta']).to match(/cl=[0-9.]+/)
176
+ end
177
+ end
178
+ end
179
+
180
+ context 'when using custom transport implementation' do
181
+ class MyTransport
182
+ include Elasticsearch::Transport::Transport::Base
183
+ def initialize(args); end
184
+ end
185
+ let(:client) { Elasticsearch::Client.new(transport_class: MyTransport) }
186
+ let(:subject){ client.instance_variable_get("@arguments")[:transport_options][:headers] }
187
+ let(:meta_header) do
188
+ if jruby?
189
+ "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}"
190
+ else
191
+ "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION}"
192
+ end
193
+ end
194
+
195
+ it 'doesnae set any info about the implementation in the metaheader' do
196
+ expect(subject['x-elastic-client-meta']).to match(regexp)
197
+ expect(subject).to include('x-elastic-client-meta' => meta_header)
198
+ end
199
+ end
200
+
201
+ context 'when using a different service version' do
202
+ before do
203
+ stub_const("Elastic::META_HEADER_SERVICE_VERSION", [:ent, '8.0.0'])
204
+ end
205
+
206
+ let(:client) { Elasticsearch::Client.new }
207
+
208
+ it 'sets the service version in the metaheader' do
209
+ expect(subject['x-elastic-client-meta']).to match(regexp)
210
+ expect(subject['x-elastic-client-meta']).to start_with('ent=8.0.0')
211
+ end
212
+ end
213
+ end
214
+ end