elasticsearch-transport 7.5.0 → 7.17.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +26 -13
  3. data/README.md +159 -64
  4. data/Rakefile +25 -13
  5. data/elasticsearch-transport.gemspec +57 -63
  6. data/lib/elasticsearch/transport/client.rb +183 -58
  7. data/lib/elasticsearch/transport/meta_header.rb +135 -0
  8. data/lib/elasticsearch/transport/redacted.rb +16 -3
  9. data/lib/elasticsearch/transport/transport/base.rb +69 -30
  10. data/lib/elasticsearch/transport/transport/connections/collection.rb +18 -8
  11. data/lib/elasticsearch/transport/transport/connections/connection.rb +25 -9
  12. data/lib/elasticsearch/transport/transport/connections/selector.rb +16 -3
  13. data/lib/elasticsearch/transport/transport/errors.rb +17 -3
  14. data/lib/elasticsearch/transport/transport/http/curb.rb +60 -35
  15. data/lib/elasticsearch/transport/transport/http/faraday.rb +32 -9
  16. data/lib/elasticsearch/transport/transport/http/manticore.rb +51 -31
  17. data/lib/elasticsearch/transport/transport/loggable.rb +16 -3
  18. data/lib/elasticsearch/transport/transport/response.rb +16 -4
  19. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +16 -3
  20. data/lib/elasticsearch/transport/transport/sniffer.rb +35 -15
  21. data/lib/elasticsearch/transport/version.rb +17 -4
  22. data/lib/elasticsearch/transport.rb +35 -33
  23. data/lib/elasticsearch-transport.rb +16 -3
  24. data/spec/elasticsearch/connections/collection_spec.rb +28 -3
  25. data/spec/elasticsearch/connections/selector_spec.rb +16 -3
  26. data/spec/elasticsearch/transport/base_spec.rb +104 -43
  27. data/spec/elasticsearch/transport/client_spec.rb +727 -163
  28. data/spec/elasticsearch/transport/http/curb_spec.rb +126 -0
  29. data/spec/elasticsearch/transport/http/faraday_spec.rb +141 -0
  30. data/spec/elasticsearch/transport/http/manticore_spec.rb +143 -0
  31. data/spec/elasticsearch/transport/meta_header_spec.rb +301 -0
  32. data/spec/elasticsearch/transport/sniffer_spec.rb +16 -16
  33. data/spec/spec_helper.rb +28 -6
  34. data/test/integration/jruby_test.rb +43 -0
  35. data/test/integration/transport_test.rb +46 -29
  36. data/test/profile/client_benchmark_test.rb +16 -3
  37. data/test/test_helper.rb +22 -25
  38. data/test/unit/connection_test.rb +23 -5
  39. data/test/unit/response_test.rb +18 -5
  40. data/test/unit/serializer_test.rb +16 -3
  41. data/test/unit/transport_base_test.rb +33 -11
  42. data/test/unit/transport_curb_test.rb +16 -4
  43. data/test/unit/transport_faraday_test.rb +18 -5
  44. data/test/unit/transport_manticore_test.rb +258 -158
  45. metadata +80 -71
@@ -1,18 +1,32 @@
1
- # Licensed to Elasticsearch B.V under one or more agreements.
2
- # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
- # See the LICENSE file in the project root for more information
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
4
17
 
5
18
  require 'base64'
19
+ require 'elasticsearch/transport/meta_header'
6
20
 
7
21
  module Elasticsearch
8
22
  module Transport
9
-
10
23
  # Handles communication with an Elasticsearch cluster.
11
24
  #
12
25
  # See {file:README.md README} for usage and code examples.
13
26
  #
14
27
  class Client
15
- DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday
28
+ include MetaHeader
29
+ DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday
16
30
 
17
31
  DEFAULT_LOGGER = lambda do
18
32
  require 'logger'
@@ -36,9 +50,15 @@ module Elasticsearch
36
50
  DEFAULT_HOST = 'localhost:9200'.freeze
37
51
 
38
52
  # The default port to use if connecting using a Cloud ID.
53
+ # Updated from 9243 to 443 in client version 7.10.1
39
54
  #
40
55
  # @since 7.2.0
41
- DEFAULT_CLOUD_PORT = 9243
56
+ DEFAULT_CLOUD_PORT = 443
57
+
58
+ # The default port to use if not otherwise specified.
59
+ #
60
+ # @since 7.2.0
61
+ DEFAULT_PORT = 9200
42
62
 
43
63
  # Returns the transport object.
44
64
  #
@@ -72,6 +92,8 @@ module Elasticsearch
72
92
  #
73
93
  # @option arguments [Boolean,Number] :retry_on_failure Retry X times when request fails before raising and
74
94
  # exception (false by default)
95
+ # @option arguments [Number] :delay_on_retry Delay in milliseconds between each retry (0 by default)
96
+ #
75
97
  # @option arguments Array<Number> :retry_on_status Retry when specific status codes are returned
76
98
  #
77
99
  # @option arguments [Boolean] :reload_on_failure Reload connections after failure (false by default)
@@ -99,6 +121,15 @@ module Elasticsearch
99
121
  # The default is false. Responses will automatically be inflated if they are compressed.
100
122
  # If a custom transport object is used, it must handle the request compression and response inflation.
101
123
  #
124
+ # @option api_key [String, Hash] :api_key Use API Key Authentication, either the base64 encoding of `id` and `api_key`
125
+ # joined by a colon as a String, or a hash with the `id` and `api_key` values.
126
+ # @option opaque_id_prefix [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client.
127
+ # This will be prepended to the id you set before each request
128
+ # if you're using X-Opaque-Id
129
+ # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
130
+ # (Default: true)
131
+ # @option ca_fingerprint [String] :ca_fingerprint provide this value to only trust certificates that are signed by a specific CA certificate
132
+ #
102
133
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
103
134
  #
104
135
  def initialize(arguments={}, &block)
@@ -108,61 +139,140 @@ module Elasticsearch
108
139
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
109
140
  @arguments[:reload_connections] ||= false
110
141
  @arguments[:retry_on_failure] ||= false
142
+ @arguments[:delay_on_retry] ||= 0
111
143
  @arguments[:reload_on_failure] ||= false
112
144
  @arguments[:randomize_hosts] ||= false
113
145
  @arguments[:transport_options] ||= {}
114
146
  @arguments[:http] ||= {}
147
+ @arguments[:enable_meta_header] = arguments.fetch(:enable_meta_header) { true }
115
148
  @options[:http] ||= {}
116
149
 
150
+ set_api_key if (@api_key = @arguments[:api_key])
151
+ set_compatibility_header if ENV['ELASTIC_CLIENT_APIVERSIONING']
152
+
117
153
  @seeds = extract_cloud_creds(@arguments)
118
154
  @seeds ||= __extract_hosts(@arguments[:hosts] ||
119
- @arguments[:host] ||
120
- @arguments[:url] ||
121
- @arguments[:urls] ||
122
- ENV['ELASTICSEARCH_URL'] ||
123
- DEFAULT_HOST)
155
+ @arguments[:host] ||
156
+ @arguments[:url] ||
157
+ @arguments[:urls] ||
158
+ ENV['ELASTICSEARCH_URL'] ||
159
+ DEFAULT_HOST)
124
160
 
125
161
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
162
+ @opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
163
+ @ca_fingerprint = @arguments.delete(:ca_fingerprint)
126
164
 
127
165
  if @arguments[:request_timeout]
128
- @arguments[:transport_options][:request] = { :timeout => @arguments[:request_timeout] }
166
+ @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
129
167
  end
130
168
 
131
169
  if @arguments[:transport]
132
170
  @transport = @arguments[:transport]
133
171
  else
134
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
135
- if transport_class == Transport::HTTP::Faraday
136
- @transport = transport_class.new(:hosts => @seeds, :options => @arguments) do |faraday|
137
- block.call faraday if block
138
- unless (h = faraday.builder.handlers.last) && h.name.start_with?("Faraday::Adapter")
139
- faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
140
- end
141
- end
142
- else
143
- @transport = transport_class.new(:hosts => @seeds, :options => @arguments)
144
- end
172
+ @transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
173
+ @transport = if @transport_class == Transport::HTTP::Faraday
174
+ @arguments[:adapter] ||= __auto_detect_adapter
175
+ set_meta_header # from include MetaHeader
176
+ @transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
177
+ faraday.adapter(@arguments[:adapter])
178
+ block&.call faraday
179
+ end
180
+ else
181
+ set_meta_header # from include MetaHeader
182
+ @transport_class.new(hosts: @seeds, options: @arguments)
183
+ end
145
184
  end
146
185
  end
147
186
 
148
187
  # Performs a request through delegation to {#transport}.
149
188
  #
150
- def perform_request(method, path, params={}, body=nil, headers=nil)
189
+ def perform_request(method, path, params = {}, body = nil, headers = nil)
151
190
  method = @send_get_body_as if 'GET' == method && body
191
+ if (opaque_id = params.delete(:opaque_id))
192
+ headers = {} if headers.nil?
193
+ opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
194
+ headers.merge!('X-Opaque-Id' => opaque_id)
195
+ end
196
+ validate_ca_fingerprints if @ca_fingerprint
152
197
  transport.perform_request(method, path, params, body, headers)
153
198
  end
154
199
 
155
200
  private
156
201
 
202
+ def set_api_key
203
+ @api_key = __encode(@api_key) if @api_key.is_a? Hash
204
+ add_header('Authorization' => "ApiKey #{@api_key}")
205
+ @arguments.delete(:user)
206
+ @arguments.delete(:password)
207
+ end
208
+
209
+ def set_compatibility_header
210
+ return unless ['1', 'true'].include?(ENV['ELASTIC_CLIENT_APIVERSIONING'])
211
+ return if instance_variable_get('@options').dig(:transport_options, :headers, 'Accept')
212
+
213
+ add_header(
214
+ {
215
+ 'Accept' => 'application/vnd.elasticsearch+json; compatible-with=7',
216
+ 'Content-Type' => 'application/vnd.elasticsearch+json; compatible-with=7'
217
+ }
218
+ )
219
+ end
220
+
221
+ def validate_ca_fingerprints
222
+ transport.connections.connections.each do |connection|
223
+ unless connection.host[:scheme] == 'https'
224
+ raise Elasticsearch::Transport::Transport::Error, 'CA fingerprinting can\'t be configured over http'
225
+ end
226
+
227
+ next if connection.verified
228
+
229
+ ctx = OpenSSL::SSL::SSLContext.new
230
+ socket = TCPSocket.new(connection.host[:host], connection.host[:port])
231
+ ssl = OpenSSL::SSL::SSLSocket.new(socket, ctx)
232
+ ssl.connect
233
+ cert_store = ssl.peer_cert_chain
234
+ matching_certs = cert_store.select do |cert|
235
+ OpenSSL::Digest::SHA256.hexdigest(cert.to_der).upcase == @ca_fingerprint.upcase.gsub(':', '')
236
+ end
237
+ if matching_certs.empty?
238
+ raise Elasticsearch::Transport::Transport::Error,
239
+ 'Server certificate CA fingerprint does not match the value configured in ca_fingerprint'
240
+ end
241
+
242
+ connection.verified = true
243
+ end
244
+ end
245
+
246
+ def add_header(header)
247
+ headers = @arguments[:transport_options]&.[](:headers) || {}
248
+ headers.merge!(header)
249
+ @arguments[:transport_options].merge!(
250
+ headers: headers
251
+ )
252
+ end
253
+
157
254
  def extract_cloud_creds(arguments)
158
- return unless arguments[:cloud_id]
255
+ return unless arguments[:cloud_id] && !arguments[:cloud_id].empty?
256
+
159
257
  name = arguments[:cloud_id].split(':')[0]
160
258
  cloud_url, elasticsearch_instance = Base64.decode64(arguments[:cloud_id].gsub("#{name}:", '')).split('$')
161
- [ { scheme: 'https',
259
+
260
+ if cloud_url.include?(':')
261
+ url, port = cloud_url.split(':')
262
+ host = "#{elasticsearch_instance}.#{url}"
263
+ else
264
+ host = "#{elasticsearch_instance}.#{cloud_url}"
265
+ port = arguments[:port] || DEFAULT_CLOUD_PORT
266
+ end
267
+ [
268
+ {
269
+ scheme: 'https',
162
270
  user: arguments[:user],
163
271
  password: arguments[:password],
164
- host: "#{elasticsearch_instance}.#{cloud_url}",
165
- port: arguments[:port] || DEFAULT_CLOUD_PORT } ]
272
+ host: host,
273
+ port: port.to_i
274
+ }
275
+ ]
166
276
  end
167
277
 
168
278
  # Normalizes and returns hosts configuration.
@@ -195,39 +305,47 @@ module Elasticsearch
195
305
 
196
306
  def __parse_host(host)
197
307
  host_parts = case host
198
- when String
199
- if host =~ /^[a-z]+\:\/\//
200
- # Construct a new `URI::Generic` directly from the array returned by URI::split.
201
- # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
202
- uri = URI::Generic.new(*URI.split(host))
203
-
204
- { :scheme => uri.scheme,
205
- :user => uri.user,
206
- :password => uri.password,
207
- :host => uri.host,
208
- :path => uri.path,
209
- :port => uri.port }
210
- else
211
- host, port = host.split(':')
212
- { :host => host,
213
- :port => port }
214
- end
215
- when URI
216
- { :scheme => host.scheme,
217
- :user => host.user,
218
- :password => host.password,
219
- :host => host.host,
220
- :path => host.path,
221
- :port => host.port }
222
- when Hash
223
- host
308
+ when String
309
+ if host =~ /^[a-z]+\:\/\//
310
+ # Construct a new `URI::Generic` directly from the array returned by URI::split.
311
+ # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
312
+ uri = URI::Generic.new(*URI.split(host))
313
+ default_port = uri.scheme == 'https' ? 443 : DEFAULT_PORT
314
+ {
315
+ scheme: uri.scheme,
316
+ user: uri.user,
317
+ password: uri.password,
318
+ host: uri.host,
319
+ path: uri.path,
320
+ port: uri.port || default_port
321
+ }
322
+ else
323
+ host, port = host.split(':')
324
+ { host: host, port: port }
325
+ end
326
+ when URI
327
+ {
328
+ scheme: host.scheme,
329
+ user: host.user,
330
+ password: host.password,
331
+ host: host.host,
332
+ path: host.path,
333
+ port: host.port
334
+ }
335
+ when Hash
336
+ host
337
+ else
338
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
339
+ end
340
+ if @api_key
341
+ # Remove Basic Auth if using API KEY
342
+ host_parts.delete(:user)
343
+ host_parts.delete(:password)
224
344
  else
225
- raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
345
+ @options[:http][:user] ||= host_parts[:user]
346
+ @options[:http][:password] ||= host_parts[:password]
226
347
  end
227
348
 
228
- @options[:http][:user] ||= host_parts[:user]
229
- @options[:http][:password] ||= host_parts[:password]
230
-
231
349
  host_parts[:port] = host_parts[:port].to_i if host_parts[:port]
232
350
  host_parts[:path].chomp!('/') if host_parts[:path]
233
351
  host_parts
@@ -255,6 +373,13 @@ module Elasticsearch
255
373
  ::Faraday.default_adapter
256
374
  end
257
375
  end
376
+
377
+ # Encode credentials for the Authorization Header
378
+ # Credentials is the base64 encoding of id and api_key joined by a colon
379
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html
380
+ def __encode(api_key)
381
+ Base64.strict_encode64([api_key[:id], api_key[:api_key]].join(':'))
382
+ end
258
383
  end
259
384
  end
260
385
  end
@@ -0,0 +1,135 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'base64'
19
+
20
+ module Elasticsearch
21
+ module Transport
22
+ # Methods for the Elastic meta header used by Cloud.
23
+ # X-Elastic-Client-Meta HTTP header which is used by Elastic Cloud and can be disabled when
24
+ # instantiating the Client with the :enable_meta_header parameter set to `false`.
25
+ #
26
+ module MetaHeader
27
+ def set_meta_header
28
+ return if @arguments[:enable_meta_header] == false
29
+
30
+ service, version = meta_header_service_version
31
+
32
+ meta_headers = {
33
+ service.to_sym => version,
34
+ rb: RUBY_VERSION,
35
+ t: Elasticsearch::Transport::VERSION
36
+ }
37
+ meta_headers.merge!(meta_header_engine) if meta_header_engine
38
+ meta_headers.merge!(meta_header_adapter) if meta_header_adapter
39
+
40
+ add_header({ 'x-elastic-client-meta' => meta_headers.map { |k, v| "#{k}=#{v}" }.join(',') })
41
+ end
42
+
43
+ def meta_header_service_version
44
+ if enterprise_search?
45
+ Elastic::ENTERPRISE_SERVICE_VERSION
46
+ elsif elasticsearch?
47
+ Elastic::ELASTICSEARCH_SERVICE_VERSION
48
+ elsif defined?(Elasticsearch::VERSION)
49
+ [:es, client_meta_version(Elasticsearch::VERSION)]
50
+ else
51
+ [:es, client_meta_version(Elasticsearch::Transport::VERSION)]
52
+ end
53
+ end
54
+
55
+ def enterprise_search?
56
+ defined?(Elastic::ENTERPRISE_SERVICE_VERSION) &&
57
+ called_from?('enterprise-search-ruby')
58
+ end
59
+
60
+ def elasticsearch?
61
+ defined?(Elastic::ELASTICSEARCH_SERVICE_VERSION) &&
62
+ called_from?('elasticsearch')
63
+ end
64
+
65
+ def called_from?(service)
66
+ !caller.select { |c| c.match?(service) }.empty?
67
+ end
68
+
69
+ # We return the current version if it's a release, but if it's a pre/alpha/beta release we
70
+ # return <VERSION_NUMBER>p
71
+ #
72
+ def client_meta_version(version)
73
+ regexp = /^([0-9]+\.[0-9]+\.[0-9]+)(\.?[a-z0-9.-]+)?$/
74
+ match = version.match(regexp)
75
+ return "#{match[1]}p" if (match[2])
76
+
77
+ version
78
+ end
79
+
80
+ def meta_header_engine
81
+ case RUBY_ENGINE
82
+ when 'ruby'
83
+ {}
84
+ when 'jruby'
85
+ { jv: ENV_JAVA['java.version'], jr: JRUBY_VERSION }
86
+ when 'rbx'
87
+ { rbx: RUBY_VERSION }
88
+ else
89
+ { RUBY_ENGINE.to_sym => RUBY_VERSION }
90
+ end
91
+ end
92
+
93
+ # This function tries to define the version for the Faraday adapter. If it hasn't been loaded
94
+ # by the time we're calling this method, it's going to report the adapter (if we know it) but
95
+ # return 0 as the version. It won't report anything when using a custom adapter we don't
96
+ # identify.
97
+ #
98
+ # Returns a Hash<adapter_alias, version>
99
+ #
100
+ def meta_header_adapter
101
+ if @transport_class == Transport::HTTP::Faraday
102
+ version = '0'
103
+ adapter_version = case @arguments[:adapter]
104
+ when :patron
105
+ version = Patron::VERSION if defined?(::Patron::VERSION)
106
+ {pt: version}
107
+ when :net_http
108
+ version = if defined?(Net::HTTP::VERSION)
109
+ Net::HTTP::VERSION
110
+ elsif defined?(Net::HTTP::HTTPVersion)
111
+ Net::HTTP::HTTPVersion
112
+ end
113
+ {nh: version}
114
+ when :typhoeus
115
+ version = Typhoeus::VERSION if defined?(::Typhoeus::VERSION)
116
+ {ty: version}
117
+ when :httpclient
118
+ version = HTTPClient::VERSION if defined?(HTTPClient::VERSION)
119
+ {hc: version}
120
+ when :net_http_persistent
121
+ version = Net::HTTP::Persistent::VERSION if defined?(Net::HTTP::Persistent::VERSION)
122
+ {np: version}
123
+ else
124
+ {}
125
+ end
126
+ {fd: Faraday::VERSION}.merge(adapter_version)
127
+ elsif defined?(Transport::HTTP::Curb) && @transport_class == Transport::HTTP::Curb
128
+ {cl: Curl::CURB_VERSION}
129
+ elsif defined?(Transport::HTTP::Manticore) && @transport_class == Transport::HTTP::Manticore
130
+ {mc: Manticore::VERSION}
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -1,6 +1,19 @@
1
- # Licensed to Elasticsearch B.V under one or more agreements.
2
- # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
- # See the LICENSE file in the project root for more information
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
4
17
 
5
18
  module Elasticsearch
6
19
  module Transport