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,120 @@
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
+ module Elastic
19
+ module Transport
20
+ module Transport
21
+ module HTTP
22
+ # Alternative HTTP transport implementation, using the [_Curb_](https://rubygems.org/gems/curb) client.
23
+ #
24
+ # @see Transport::Base
25
+ #
26
+ class Curb
27
+ include Base
28
+
29
+ # Performs the request by invoking {Transport::Base#perform_request} with a block.
30
+ #
31
+ # @return [Response]
32
+ # @see Transport::Base#perform_request
33
+ #
34
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
35
+ super do |connection, url|
36
+ connection.connection.url = connection.full_url(path, params)
37
+
38
+ case method
39
+ when 'HEAD'
40
+ connection.connection.set :nobody, true
41
+ when 'GET', 'POST', 'PUT', 'DELETE'
42
+ connection.connection.set :nobody, false
43
+
44
+ connection.connection.put_data = __convert_to_json(body) if body
45
+
46
+ if headers
47
+ if connection.connection.headers
48
+ connection.connection.headers.merge!(headers)
49
+ else
50
+ connection.connection.headers = headers
51
+ end
52
+ end
53
+
54
+ else raise ArgumentError, "Unsupported HTTP method: #{method}"
55
+ end
56
+
57
+ connection.connection.http(method.to_sym)
58
+
59
+ response_headers = {}
60
+ response_headers['content-type'] = 'application/json' if connection.connection.header_str =~ /\/json/
61
+
62
+ Response.new connection.connection.response_code,
63
+ decompress_response(connection.connection.body_str),
64
+ response_headers
65
+ end
66
+ end
67
+
68
+ # Builds and returns a connection
69
+ #
70
+ # @return [Connections::Connection]
71
+ #
72
+ def __build_connection(host, options={}, block=nil)
73
+ client = ::Curl::Easy.new
74
+
75
+ apply_headers(client, options)
76
+ client.url = __full_url(host)
77
+
78
+ if host[:user]
79
+ client.http_auth_types = host[:auth_type] || :basic
80
+ client.username = host[:user]
81
+ client.password = host[:password]
82
+ end
83
+
84
+ client.instance_eval(&block) if block
85
+
86
+ Connections::Connection.new :host => host, :connection => client
87
+ end
88
+
89
+ # Returns an array of implementation specific connection errors.
90
+ #
91
+ # @return [Array]
92
+ #
93
+ def host_unreachable_exceptions
94
+ [
95
+ ::Curl::Err::HostResolutionError,
96
+ ::Curl::Err::ConnectionFailedError,
97
+ ::Curl::Err::GotNothingError,
98
+ ::Curl::Err::RecvError,
99
+ ::Curl::Err::SendError,
100
+ ::Curl::Err::TimeoutError
101
+ ]
102
+ end
103
+
104
+ private
105
+
106
+ def user_agent_header(client)
107
+ @user_agent ||= begin
108
+ meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
109
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
110
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
111
+ end
112
+ meta << "Curb #{Curl::CURB_VERSION}"
113
+ "elastic-transport-ruby/#{VERSION} (#{meta.join('; ')})"
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,95 @@
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
+ module Elastic
19
+ module Transport
20
+ module Transport
21
+ module HTTP
22
+ # The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday)
23
+ # library for abstracting the HTTP client.
24
+ #
25
+ # @see Transport::Base
26
+ #
27
+ class Faraday
28
+ include Base
29
+
30
+ # Performs the request by invoking {Transport::Base#perform_request} with a block.
31
+ #
32
+ # @return [Response]
33
+ # @see Transport::Base#perform_request
34
+ #
35
+ def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
36
+ super do |connection, url|
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
+ response = connection.connection.run_request(method.downcase.to_sym,
48
+ url,
49
+ ( body ? __convert_to_json(body) : nil ),
50
+ headers)
51
+
52
+ Response.new response.status, decompress_response(response.body), response.headers
53
+ end
54
+ end
55
+
56
+ # Builds and returns a connection
57
+ #
58
+ # @return [Connections::Connection]
59
+ #
60
+ def __build_connection(host, options={}, block=nil)
61
+ client = ::Faraday.new(__full_url(host), options, &block)
62
+ apply_headers(client, options)
63
+ Connections::Connection.new :host => host, :connection => client
64
+ end
65
+
66
+ # Returns an array of implementation specific connection errors.
67
+ #
68
+ # @return [Array]
69
+ #
70
+ def host_unreachable_exceptions
71
+ [
72
+ ::Faraday::ConnectionFailed,
73
+ ::Faraday::TimeoutError,
74
+ ::Faraday.const_defined?(:ServerError) ? ::Faraday::ServerError : nil,
75
+ ::Faraday::SSLError
76
+ ].compact
77
+ end
78
+
79
+ private
80
+
81
+ def user_agent_header(client)
82
+ @user_agent ||= begin
83
+ meta = ["RUBY_VERSION: #{RUBY_VERSION}"]
84
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
85
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
86
+ end
87
+ meta << "#{client.headers[USER_AGENT_STR]}"
88
+ "elastic-transport-ruby/#{VERSION} (#{meta.join('; ')})"
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,179 @@
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 'manticore'
19
+
20
+ module Elastic
21
+ module Transport
22
+ module Transport
23
+ module HTTP
24
+ # Alternative HTTP transport implementation for JRuby,
25
+ # using the [_Manticore_](https://github.com/cheald/manticore) client,
26
+ #
27
+ # @example HTTP
28
+ #
29
+ # require 'elasticsearch/transport/transport/http/manticore'
30
+ #
31
+ # client = Elasticsearch::Client.new transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore
32
+ #
33
+ # client.transport.connections.first.connection
34
+ # => #<Manticore::Client:0x56bf7ca6 ...>
35
+ #
36
+ # client.info['status']
37
+ # => 200
38
+ #
39
+ # @example HTTPS (All SSL settings are optional,
40
+ # see http://www.rubydoc.info/gems/manticore/Manticore/Client:initialize)
41
+ #
42
+ # require 'elasticsearch/transport/transport/http/manticore'
43
+ #
44
+ # client = Elasticsearch::Client.new \
45
+ # url: 'https://elasticsearch.example.com',
46
+ # transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore,
47
+ # ssl: {
48
+ # truststore: '/tmp/truststore.jks',
49
+ # truststore_password: 'password',
50
+ # keystore: '/tmp/keystore.jks',
51
+ # keystore_password: 'secret',
52
+ # }
53
+ #
54
+ # client.transport.connections.first.connection
55
+ # => #<Manticore::Client:0xdeadbeef ...>
56
+ #
57
+ # client.info['status']
58
+ # => 200
59
+ #
60
+ # @see Transport::Base
61
+ #
62
+ class Manticore
63
+ include Base
64
+
65
+ def initialize(arguments={}, &block)
66
+ @request_options = { headers: (arguments.dig(:transport_options, :headers) || {}) }
67
+ @manticore = build_client(arguments[:options] || {})
68
+ super(arguments, &block)
69
+ end
70
+
71
+ # Should just be run once at startup
72
+ def build_client(options={})
73
+ client_options = options[:transport_options] || {}
74
+ client_options[:ssl] = options[:ssl] || {}
75
+
76
+ @manticore = ::Manticore::Client.new(client_options)
77
+ end
78
+
79
+ # Performs the request by invoking {Transport::Base#perform_request} with a block.
80
+ #
81
+ # @return [Response]
82
+ # @see Transport::Base#perform_request
83
+ #
84
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
85
+ super do |connection, url|
86
+ params[:body] = __convert_to_json(body) if body
87
+ params[:headers] = headers if headers
88
+ params = params.merge @request_options
89
+ case method
90
+ when "GET"
91
+ resp = connection.connection.get(url, params)
92
+ when "HEAD"
93
+ resp = connection.connection.head(url, params)
94
+ when "PUT"
95
+ resp = connection.connection.put(url, params)
96
+ when "POST"
97
+ resp = connection.connection.post(url, params)
98
+ when "DELETE"
99
+ resp = connection.connection.delete(url, params)
100
+ else
101
+ raise ArgumentError.new "Method #{method} not supported"
102
+ end
103
+ Response.new resp.code, resp.read_body, resp.headers
104
+ end
105
+ end
106
+
107
+ # Builds and returns a collection of connections.
108
+ # Each connection is a Manticore::Client
109
+ #
110
+ # @return [Connections::Collection]
111
+ #
112
+ def __build_connections
113
+ apply_headers(@request_options, options[:transport_options])
114
+ apply_headers(@request_options, options)
115
+
116
+ Connections::Collection.new \
117
+ :connections => hosts.map { |host|
118
+ host[:protocol] = host[:scheme] || DEFAULT_PROTOCOL
119
+ host[:port] ||= DEFAULT_PORT
120
+
121
+ host.delete(:user) # auth is not supported here.
122
+ host.delete(:password) # use the headers
123
+
124
+ Connections::Connection.new \
125
+ :host => host,
126
+ :connection => @manticore
127
+ },
128
+ :selector_class => options[:selector_class],
129
+ :selector => options[:selector]
130
+ end
131
+
132
+ # Closes all connections by marking them as dead
133
+ # and closing the underlying HttpClient instances
134
+ #
135
+ # @return [Connections::Collection]
136
+ #
137
+ def __close_connections
138
+ # The Manticore adapter uses a single long-lived instance
139
+ # of Manticore::Client, so we don't close the connections.
140
+ end
141
+
142
+ # Returns an array of implementation specific connection errors.
143
+ #
144
+ # @return [Array]
145
+ #
146
+ def host_unreachable_exceptions
147
+ [
148
+ ::Manticore::Timeout,
149
+ ::Manticore::SocketException,
150
+ ::Manticore::ClientProtocolException,
151
+ ::Manticore::ResolutionFailure
152
+ ]
153
+ end
154
+
155
+ private
156
+
157
+ def apply_headers(request_options, options)
158
+ headers = options&.[](:headers) || {}
159
+ headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
160
+ headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header
161
+ headers[ACCEPT_ENCODING] = GZIP if use_compression?
162
+ request_options[:headers].merge!(headers)
163
+ end
164
+
165
+ def user_agent_header
166
+ @user_agent ||= begin
167
+ meta = ["RUBY_VERSION: #{JRUBY_VERSION}"]
168
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
169
+ meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
170
+ end
171
+ meta << "Manticore #{::Manticore::VERSION}"
172
+ "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})"
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,83 @@
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
+ module Elastic
19
+ # Module to encapsulate all logging functionality.
20
+ #
21
+ # @since 7.0.0
22
+ module Loggable
23
+ # Log a debug message.
24
+ #
25
+ # @example Log a debug message.
26
+ # log_debug('Message')
27
+ #
28
+ # @param [ String ] message The message to log.
29
+ #
30
+ # @since 7.0.0
31
+ def log_debug(message)
32
+ logger.debug(message) if logger && logger.debug?
33
+ end
34
+
35
+ # Log an error message.
36
+ #
37
+ # @example Log an error message.
38
+ # log_error('Message')
39
+ #
40
+ # @param [ String ] message The message to log.
41
+ #
42
+ # @since 7.0.0
43
+ def log_error(message)
44
+ logger.error(message) if logger && logger.error?
45
+ end
46
+
47
+ # Log a fatal message.
48
+ #
49
+ # @example Log a fatal message.
50
+ # log_fatal('Message')
51
+ #
52
+ # @param [ String ] message The message to log.
53
+ #
54
+ # @since 7.0.0
55
+ def log_fatal(message)
56
+ logger.fatal(message) if logger && logger.fatal?
57
+ end
58
+
59
+ # Log an info message.
60
+ #
61
+ # @example Log an info message.
62
+ # log_info('Message')
63
+ #
64
+ # @param [ String ] message The message to log.
65
+ #
66
+ # @since 7.0.0
67
+ def log_info(message)
68
+ logger.info(message) if logger && logger.info?
69
+ end
70
+
71
+ # Log a warn message.
72
+ #
73
+ # @example Log a warn message.
74
+ # log_warn('Message')
75
+ #
76
+ # @param [ String ] message The message to log.
77
+ #
78
+ # @since 7.0.0
79
+ def log_warn(message)
80
+ logger.warn(message) if logger && logger.warn?
81
+ end
82
+ end
83
+ end