elastic-transport 8.0.0.pre1

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 (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