elasticsearch-transport 7.5.0 → 7.17.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +26 -13
- data/README.md +159 -64
- data/Rakefile +25 -13
- data/elasticsearch-transport.gemspec +57 -63
- data/lib/elasticsearch/transport/client.rb +183 -58
- data/lib/elasticsearch/transport/meta_header.rb +135 -0
- data/lib/elasticsearch/transport/redacted.rb +16 -3
- data/lib/elasticsearch/transport/transport/base.rb +69 -30
- data/lib/elasticsearch/transport/transport/connections/collection.rb +18 -8
- data/lib/elasticsearch/transport/transport/connections/connection.rb +25 -9
- data/lib/elasticsearch/transport/transport/connections/selector.rb +16 -3
- data/lib/elasticsearch/transport/transport/errors.rb +17 -3
- data/lib/elasticsearch/transport/transport/http/curb.rb +60 -35
- data/lib/elasticsearch/transport/transport/http/faraday.rb +32 -9
- data/lib/elasticsearch/transport/transport/http/manticore.rb +51 -31
- data/lib/elasticsearch/transport/transport/loggable.rb +16 -3
- data/lib/elasticsearch/transport/transport/response.rb +16 -4
- data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +16 -3
- data/lib/elasticsearch/transport/transport/sniffer.rb +35 -15
- data/lib/elasticsearch/transport/version.rb +17 -4
- data/lib/elasticsearch/transport.rb +35 -33
- data/lib/elasticsearch-transport.rb +16 -3
- data/spec/elasticsearch/connections/collection_spec.rb +28 -3
- data/spec/elasticsearch/connections/selector_spec.rb +16 -3
- data/spec/elasticsearch/transport/base_spec.rb +104 -43
- data/spec/elasticsearch/transport/client_spec.rb +727 -163
- data/spec/elasticsearch/transport/http/curb_spec.rb +126 -0
- data/spec/elasticsearch/transport/http/faraday_spec.rb +141 -0
- data/spec/elasticsearch/transport/http/manticore_spec.rb +143 -0
- data/spec/elasticsearch/transport/meta_header_spec.rb +301 -0
- data/spec/elasticsearch/transport/sniffer_spec.rb +16 -16
- data/spec/spec_helper.rb +28 -6
- data/test/integration/jruby_test.rb +43 -0
- data/test/integration/transport_test.rb +46 -29
- data/test/profile/client_benchmark_test.rb +16 -3
- data/test/test_helper.rb +22 -25
- data/test/unit/connection_test.rb +23 -5
- data/test/unit/response_test.rb +18 -5
- data/test/unit/serializer_test.rb +16 -3
- data/test/unit/transport_base_test.rb +33 -11
- data/test/unit/transport_curb_test.rb +16 -4
- data/test/unit/transport_faraday_test.rb +18 -5
- data/test/unit/transport_manticore_test.rb +258 -158
- metadata +80 -71
@@ -1,11 +1,23 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
7
20
|
module Transport
|
8
|
-
|
9
21
|
# @abstract Module with common functionality for transport implementations.
|
10
22
|
#
|
11
23
|
module Base
|
@@ -34,13 +46,14 @@ module Elasticsearch
|
|
34
46
|
#
|
35
47
|
# @see Client#initialize
|
36
48
|
#
|
37
|
-
def initialize(arguments={}, &block)
|
49
|
+
def initialize(arguments = {}, &block)
|
38
50
|
@state_mutex = Mutex.new
|
39
51
|
|
40
52
|
@hosts = arguments[:hosts] || []
|
41
53
|
@options = arguments[:options] || {}
|
42
54
|
@options[:http] ||= {}
|
43
55
|
@options[:retry_on_status] ||= []
|
56
|
+
@options[:delay_on_retry] ||= 0
|
44
57
|
|
45
58
|
@block = block
|
46
59
|
@compression = !!@options[:compression]
|
@@ -210,7 +223,7 @@ module Elasticsearch
|
|
210
223
|
# @api private
|
211
224
|
#
|
212
225
|
def __convert_to_json(o=nil, options={})
|
213
|
-
o
|
226
|
+
o.is_a?(String) ? o : serializer.dump(o, options)
|
214
227
|
end
|
215
228
|
|
216
229
|
# Returns a full URL based on information from host
|
@@ -221,9 +234,9 @@ module Elasticsearch
|
|
221
234
|
def __full_url(host)
|
222
235
|
url = "#{host[:protocol]}://"
|
223
236
|
url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
|
224
|
-
url +=
|
237
|
+
url += host[:host]
|
225
238
|
url += ":#{host[:port]}" if host[:port]
|
226
|
-
url +=
|
239
|
+
url += host[:path] if host[:path]
|
227
240
|
url
|
228
241
|
end
|
229
242
|
|
@@ -245,11 +258,13 @@ module Elasticsearch
|
|
245
258
|
# @raise [ServerError] If request failed on server
|
246
259
|
# @raise [Error] If no connection is available
|
247
260
|
#
|
248
|
-
def perform_request(method, path, params={}, body=nil, headers=nil, opts={}, &block)
|
249
|
-
raise NoMethodError,
|
261
|
+
def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {}, &block)
|
262
|
+
raise NoMethodError, 'Implement this method in your transport class' unless block_given?
|
263
|
+
|
250
264
|
start = Time.now
|
251
265
|
tries = 0
|
252
266
|
reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
|
267
|
+
delay_on_retry = opts.fetch(:delay_on_retry, @options[:delay_on_retry])
|
253
268
|
|
254
269
|
max_retries = if opts.key?(:retry_on_failure)
|
255
270
|
opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
|
@@ -258,21 +273,19 @@ module Elasticsearch
|
|
258
273
|
end
|
259
274
|
|
260
275
|
params = params.clone
|
261
|
-
|
262
276
|
ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i }
|
263
277
|
|
264
278
|
begin
|
279
|
+
sleep(delay_on_retry / 1000.0) if tries > 0
|
265
280
|
tries += 1
|
266
|
-
connection = get_connection or raise Error.new(
|
281
|
+
connection = get_connection or raise Error.new('Cannot get new connection from pool.')
|
267
282
|
|
268
283
|
if connection.connection.respond_to?(:params) && connection.connection.params.respond_to?(:to_hash)
|
269
284
|
params = connection.connection.params.merge(params.to_hash)
|
270
285
|
end
|
271
286
|
|
272
|
-
url
|
273
|
-
|
274
|
-
response = block.call(connection, url)
|
275
|
-
|
287
|
+
url = connection.full_url(path, params)
|
288
|
+
response = block.call(connection, url)
|
276
289
|
connection.healthy! if connection.failures > 0
|
277
290
|
|
278
291
|
# Raise an exception so we can catch it for `retry_on_status`
|
@@ -295,7 +308,6 @@ module Elasticsearch
|
|
295
308
|
log_error "[#{e.class}] #{e.message} #{connection.host.inspect}"
|
296
309
|
|
297
310
|
connection.dead!
|
298
|
-
|
299
311
|
if reload_on_failure and tries < connections.all.size
|
300
312
|
log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})"
|
301
313
|
reload_connections! and retry
|
@@ -322,14 +334,10 @@ module Elasticsearch
|
|
322
334
|
duration = Time.now - start
|
323
335
|
|
324
336
|
if response.status.to_i >= 300
|
325
|
-
__log_response
|
326
|
-
__trace
|
327
|
-
|
337
|
+
__log_response(method, path, params, body, url, response, nil, 'N/A', duration)
|
338
|
+
__trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
|
328
339
|
# Log the failure only when `ignore` doesn't match the response status
|
329
|
-
unless ignore.include?(response.status.to_i)
|
330
|
-
log_fatal "[#{response.status}] #{response.body}"
|
331
|
-
end
|
332
|
-
|
340
|
+
log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i)
|
333
341
|
__raise_transport_error response unless ignore.include?(response.status.to_i)
|
334
342
|
end
|
335
343
|
|
@@ -340,8 +348,8 @@ module Elasticsearch
|
|
340
348
|
__log_response method, path, params, body, url, response, json, took, duration
|
341
349
|
end
|
342
350
|
|
343
|
-
__trace
|
344
|
-
|
351
|
+
__trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer
|
352
|
+
log_warn(response.headers['warning']) if response.headers&.[]('warning')
|
345
353
|
Response.new response.status, json || response.body, response.headers
|
346
354
|
ensure
|
347
355
|
@last_request_at = Time.now
|
@@ -360,17 +368,38 @@ module Elasticsearch
|
|
360
368
|
|
361
369
|
USER_AGENT_STR = 'User-Agent'.freeze
|
362
370
|
USER_AGENT_REGEX = /user\-?\_?agent/
|
371
|
+
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
372
|
+
CONTENT_ENCODING = 'Content-Encoding'.freeze
|
363
373
|
CONTENT_TYPE_STR = 'Content-Type'.freeze
|
364
374
|
CONTENT_TYPE_REGEX = /content\-?\_?type/
|
365
375
|
DEFAULT_CONTENT_TYPE = 'application/json'.freeze
|
366
376
|
GZIP = 'gzip'.freeze
|
367
|
-
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
368
377
|
GZIP_FIRST_TWO_BYTES = '1f8b'.freeze
|
369
378
|
HEX_STRING_DIRECTIVE = 'H*'.freeze
|
370
379
|
RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
|
371
380
|
|
381
|
+
def compress_request(body, headers)
|
382
|
+
if body
|
383
|
+
headers ||= {}
|
384
|
+
|
385
|
+
if gzipped?(body)
|
386
|
+
headers[CONTENT_ENCODING] = GZIP
|
387
|
+
elsif use_compression?
|
388
|
+
headers[CONTENT_ENCODING] = GZIP
|
389
|
+
gzip = Zlib::GzipWriter.new(StringIO.new)
|
390
|
+
gzip << body
|
391
|
+
body = gzip.close.string
|
392
|
+
else
|
393
|
+
headers.delete(CONTENT_ENCODING)
|
394
|
+
end
|
395
|
+
elsif headers
|
396
|
+
headers.delete(CONTENT_ENCODING)
|
397
|
+
end
|
398
|
+
|
399
|
+
[body, headers]
|
400
|
+
end
|
401
|
+
|
372
402
|
def decompress_response(body)
|
373
|
-
return body unless use_compression?
|
374
403
|
return body unless gzipped?(body)
|
375
404
|
|
376
405
|
io = StringIO.new(body)
|
@@ -383,6 +412,8 @@ module Elasticsearch
|
|
383
412
|
end
|
384
413
|
|
385
414
|
def gzipped?(body)
|
415
|
+
return unless body && !body.empty?
|
416
|
+
|
386
417
|
body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES
|
387
418
|
end
|
388
419
|
|
@@ -399,7 +430,7 @@ module Elasticsearch
|
|
399
430
|
end
|
400
431
|
|
401
432
|
def find_value(hash, regex)
|
402
|
-
key_value = hash.find { |k,
|
433
|
+
key_value = hash.find { |k, _| k.to_s.downcase =~ regex }
|
403
434
|
if key_value
|
404
435
|
hash.delete(key_value[0])
|
405
436
|
key_value[1]
|
@@ -415,7 +446,15 @@ module Elasticsearch
|
|
415
446
|
"elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
|
416
447
|
end
|
417
448
|
end
|
449
|
+
|
450
|
+
def connection_headers(connection)
|
451
|
+
if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore
|
452
|
+
@request_options[:headers]
|
453
|
+
else
|
454
|
+
connection.connection.headers
|
455
|
+
end
|
456
|
+
end
|
418
457
|
end
|
419
458
|
end
|
420
459
|
end
|
421
|
-
end
|
460
|
+
end
|
@@ -1,6 +1,19 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
@@ -65,16 +78,13 @@ module Elasticsearch
|
|
65
78
|
|
66
79
|
# Returns a connection.
|
67
80
|
#
|
68
|
-
# If there are no alive connections,
|
81
|
+
# If there are no alive connections, returns a connection with least failures.
|
69
82
|
# Delegates to selector's `#select` method to get the connection.
|
70
83
|
#
|
71
84
|
# @return [Connection]
|
72
85
|
#
|
73
86
|
def get_connection(options={})
|
74
|
-
|
75
|
-
dead_connection.alive!
|
76
|
-
end
|
77
|
-
selector.select(options)
|
87
|
+
selector.select(options) || @connections.min_by(&:failures)
|
78
88
|
end
|
79
89
|
|
80
90
|
def each(&block)
|
@@ -1,12 +1,24 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
7
20
|
module Transport
|
8
21
|
module Connections
|
9
|
-
|
10
22
|
# Wraps the connection information and logic.
|
11
23
|
#
|
12
24
|
# The Connection instance wraps the host information (hostname, port, attributes, etc),
|
@@ -21,6 +33,7 @@ module Elasticsearch
|
|
21
33
|
DEFAULT_RESURRECT_TIMEOUT = 60
|
22
34
|
|
23
35
|
attr_reader :host, :connection, :options, :failures, :dead_since
|
36
|
+
attr_accessor :verified
|
24
37
|
|
25
38
|
# @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`)
|
26
39
|
# @option arguments [Object] :connection The transport-specific physical connection or "session"
|
@@ -30,6 +43,7 @@ module Elasticsearch
|
|
30
43
|
@host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
|
31
44
|
@connection = arguments[:connection]
|
32
45
|
@options = arguments[:options] || {}
|
46
|
+
@verified = false
|
33
47
|
@state_mutex = Mutex.new
|
34
48
|
|
35
49
|
@options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT
|
@@ -41,12 +55,14 @@ module Elasticsearch
|
|
41
55
|
#
|
42
56
|
# @return [String]
|
43
57
|
#
|
44
|
-
def full_url(path, params={})
|
58
|
+
def full_url(path, params = {})
|
45
59
|
url = "#{host[:protocol]}://"
|
46
60
|
url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
|
47
61
|
url += "#{host[:host]}:#{host[:port]}"
|
48
62
|
url += "#{host[:path]}" if host[:path]
|
49
|
-
|
63
|
+
full_path = full_path(path, params)
|
64
|
+
url += '/' unless full_path.match?(/^\//)
|
65
|
+
url += full_path
|
50
66
|
end
|
51
67
|
|
52
68
|
# Returns the complete endpoint path with serialized parameters.
|
@@ -122,14 +138,15 @@ module Elasticsearch
|
|
122
138
|
}
|
123
139
|
end
|
124
140
|
|
125
|
-
# Equality operator based on connection protocol, host and
|
141
|
+
# Equality operator based on connection protocol, host, port and attributes
|
126
142
|
#
|
127
143
|
# @return [Boolean]
|
128
144
|
#
|
129
145
|
def ==(other)
|
130
146
|
self.host[:protocol] == other.host[:protocol] && \
|
131
147
|
self.host[:host] == other.host[:host] && \
|
132
|
-
self.host[:port].to_i == other.host[:port].to_i
|
148
|
+
self.host[:port].to_i == other.host[:port].to_i && \
|
149
|
+
self.host[:attributes] == other.host[:attributes]
|
133
150
|
end
|
134
151
|
|
135
152
|
# @return [String]
|
@@ -138,7 +155,6 @@ module Elasticsearch
|
|
138
155
|
"<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
|
139
156
|
end
|
140
157
|
end
|
141
|
-
|
142
158
|
end
|
143
159
|
end
|
144
160
|
end
|
@@ -1,6 +1,19 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
@@ -1,6 +1,19 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
@@ -51,6 +64,7 @@ module Elasticsearch
|
|
51
64
|
418 => 'ImATeapot',
|
52
65
|
421 => 'TooManyConnectionsFromThisIP',
|
53
66
|
426 => 'UpgradeRequired',
|
67
|
+
429 => 'TooManyRequests',
|
54
68
|
450 => 'BlockedByWindowsParentalControls',
|
55
69
|
494 => 'RequestHeaderTooLarge',
|
56
70
|
497 => 'HTTPToHTTPS',
|
@@ -1,56 +1,82 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
7
20
|
module Transport
|
8
21
|
module HTTP
|
9
|
-
|
10
22
|
# Alternative HTTP transport implementation, using the [_Curb_](https://rubygems.org/gems/curb) client.
|
11
23
|
#
|
12
24
|
# @see Transport::Base
|
13
25
|
#
|
14
26
|
class Curb
|
15
27
|
include Base
|
16
|
-
|
17
28
|
# Performs the request by invoking {Transport::Base#perform_request} with a block.
|
18
29
|
#
|
19
30
|
# @return [Response]
|
20
31
|
# @see Transport::Base#perform_request
|
21
32
|
#
|
22
33
|
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
23
|
-
super do |connection,
|
34
|
+
super do |connection, _url|
|
24
35
|
connection.connection.url = connection.full_url(path, params)
|
36
|
+
body = body ? __convert_to_json(body) : nil
|
37
|
+
body, headers = compress_request(body, headers)
|
25
38
|
|
26
39
|
case method
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
+
when 'HEAD'
|
41
|
+
connection.connection.set :nobody, true
|
42
|
+
when 'GET', 'POST', 'PUT', 'DELETE'
|
43
|
+
connection.connection.set :nobody, false
|
44
|
+
|
45
|
+
connection.connection.put_data = body if body
|
46
|
+
|
47
|
+
if headers
|
48
|
+
if connection.connection.headers
|
49
|
+
connection.connection.headers.merge!(headers)
|
50
|
+
else
|
51
|
+
connection.connection.headers = headers
|
40
52
|
end
|
41
|
-
|
42
|
-
|
53
|
+
end
|
54
|
+
else
|
55
|
+
raise ArgumentError, "Unsupported HTTP method: #{method}"
|
43
56
|
end
|
44
57
|
|
45
58
|
connection.connection.http(method.to_sym)
|
46
59
|
|
47
|
-
|
48
|
-
|
60
|
+
Response.new(
|
61
|
+
connection.connection.response_code,
|
62
|
+
decompress_response(connection.connection.body_str),
|
63
|
+
headers(connection)
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def headers(connection)
|
69
|
+
headers_string = connection.connection.header_str
|
70
|
+
return nil if headers_string.nil?
|
49
71
|
|
50
|
-
|
51
|
-
|
52
|
-
|
72
|
+
response_headers = headers_string&.split(/\\r\\n|\r\n/).reject(&:empty?)
|
73
|
+
response_headers.shift # Removes HTTP status string
|
74
|
+
processed_header = response_headers.flat_map { |s| s.scan(/^(\S+): (.+)/) }
|
75
|
+
headers_hash = Hash[processed_header].transform_keys(&:downcase)
|
76
|
+
if headers_hash['content-type']&.match?(/application\/json/)
|
77
|
+
headers_hash['content-type'] = 'application/json'
|
53
78
|
end
|
79
|
+
headers_hash
|
54
80
|
end
|
55
81
|
|
56
82
|
# Builds and returns a connection
|
@@ -59,9 +85,8 @@ module Elasticsearch
|
|
59
85
|
#
|
60
86
|
def __build_connection(host, options={}, block=nil)
|
61
87
|
client = ::Curl::Easy.new
|
62
|
-
|
63
88
|
apply_headers(client, options)
|
64
|
-
client.url
|
89
|
+
client.url = __full_url(host)
|
65
90
|
|
66
91
|
if host[:user]
|
67
92
|
client.http_auth_types = host[:auth_type] || :basic
|
@@ -93,13 +118,13 @@ module Elasticsearch
|
|
93
118
|
|
94
119
|
def user_agent_header(client)
|
95
120
|
@user_agent ||= begin
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
121
|
+
meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
|
122
|
+
if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
|
123
|
+
meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
|
124
|
+
end
|
125
|
+
meta << "Curb #{Curl::CURB_VERSION}"
|
126
|
+
"elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
|
127
|
+
end
|
103
128
|
end
|
104
129
|
end
|
105
130
|
end
|
@@ -1,12 +1,24 @@
|
|
1
|
-
# Licensed to Elasticsearch B.V under one or more
|
2
|
-
#
|
3
|
-
#
|
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
|
7
20
|
module Transport
|
8
21
|
module HTTP
|
9
|
-
|
10
22
|
# The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday)
|
11
23
|
# library for abstracting the HTTP client.
|
12
24
|
#
|
@@ -20,13 +32,24 @@ module Elasticsearch
|
|
20
32
|
# @return [Response]
|
21
33
|
# @see Transport::Base#perform_request
|
22
34
|
#
|
23
|
-
def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
|
35
|
+
def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
|
24
36
|
super do |connection, url|
|
25
|
-
headers =
|
37
|
+
headers = if connection.connection.headers
|
38
|
+
if !headers.nil?
|
39
|
+
connection.connection.headers.merge(headers)
|
40
|
+
else
|
41
|
+
connection.connection.headers
|
42
|
+
end
|
43
|
+
else
|
44
|
+
headers
|
45
|
+
end
|
46
|
+
|
47
|
+
body = body ? __convert_to_json(body) : nil
|
48
|
+
body, headers = compress_request(body, headers)
|
26
49
|
|
27
50
|
response = connection.connection.run_request(method.downcase.to_sym,
|
28
51
|
url,
|
29
|
-
|
52
|
+
body,
|
30
53
|
headers)
|
31
54
|
|
32
55
|
Response.new response.status, decompress_response(response.body), response.headers
|
@@ -40,7 +63,7 @@ module Elasticsearch
|
|
40
63
|
def __build_connection(host, options={}, block=nil)
|
41
64
|
client = ::Faraday.new(__full_url(host), options, &block)
|
42
65
|
apply_headers(client, options)
|
43
|
-
Connections::Connection.new
|
66
|
+
Connections::Connection.new(host: host, connection: client)
|
44
67
|
end
|
45
68
|
|
46
69
|
# Returns an array of implementation specific connection errors.
|
@@ -48,7 +71,7 @@ module Elasticsearch
|
|
48
71
|
# @return [Array]
|
49
72
|
#
|
50
73
|
def host_unreachable_exceptions
|
51
|
-
[::Faraday::
|
74
|
+
[::Faraday::ConnectionFailed, ::Faraday::TimeoutError]
|
52
75
|
end
|
53
76
|
|
54
77
|
private
|