elasticsearch-transport 7.4.0 → 7.17.10
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.
- checksums.yaml +4 -4
- data/Gemfile +30 -13
- data/Gemfile-faraday1.gemfile +47 -0
- data/README.md +159 -64
- data/Rakefile +63 -13
- data/elasticsearch-transport.gemspec +55 -63
- data/lib/elasticsearch/transport/client.rb +184 -59
- 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 +57 -32
- data/lib/elasticsearch/transport/transport/loggable.rb +16 -3
- data/lib/elasticsearch/transport/transport/response.rb +17 -5
- 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 +107 -49
- data/spec/elasticsearch/transport/client_spec.rb +734 -164
- 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 +161 -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 +32 -6
- data/test/integration/jruby_test.rb +43 -0
- data/test/integration/transport_test.rb +109 -46
- data/test/profile/client_benchmark_test.rb +16 -3
- data/test/test_helper.rb +26 -25
- data/test/unit/adapters_test.rb +88 -0
- 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 +64 -76
| @@ -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
         |