httpx 1.4.0 → 1.4.4
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/README.md +1 -2
- data/doc/release_notes/1_4_1.md +19 -0
- data/doc/release_notes/1_4_2.md +20 -0
- data/doc/release_notes/1_4_3.md +11 -0
- data/doc/release_notes/1_4_4.md +14 -0
- data/lib/httpx/adapters/datadog.rb +55 -83
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/adapters/webmock.rb +11 -1
- data/lib/httpx/callbacks.rb +2 -2
- data/lib/httpx/connection/http2.rb +33 -18
- data/lib/httpx/connection.rb +115 -55
- data/lib/httpx/errors.rb +3 -4
- data/lib/httpx/io/ssl.rb +6 -3
- data/lib/httpx/loggable.rb +13 -6
- data/lib/httpx/plugins/callbacks.rb +1 -0
- data/lib/httpx/plugins/circuit_breaker.rb +1 -0
- data/lib/httpx/plugins/expect.rb +1 -1
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
- data/lib/httpx/plugins/internal_telemetry.rb +21 -1
- data/lib/httpx/plugins/retries.rb +2 -2
- data/lib/httpx/plugins/stream.rb +42 -18
- data/lib/httpx/request/body.rb +9 -14
- data/lib/httpx/request.rb +37 -3
- data/lib/httpx/resolver/https.rb +4 -2
- data/lib/httpx/resolver/native.rb +111 -55
- data/lib/httpx/resolver/resolver.rb +18 -11
- data/lib/httpx/resolver/system.rb +3 -5
- data/lib/httpx/response.rb +9 -4
- data/lib/httpx/selector.rb +33 -23
- data/lib/httpx/session.rb +20 -49
- data/lib/httpx/timers.rb +16 -1
- data/lib/httpx/transcoder/body.rb +15 -31
- data/lib/httpx/transcoder/multipart/encoder.rb +2 -1
- data/lib/httpx/transcoder/multipart/part.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -1
- data/sig/callbacks.rbs +2 -2
- data/sig/connection/http2.rbs +4 -0
- data/sig/connection.rbs +19 -5
- data/sig/errors.rbs +3 -3
- data/sig/loggable.rbs +2 -2
- data/sig/plugins/stream.rbs +3 -0
- data/sig/pool.rbs +2 -0
- data/sig/request/body.rbs +0 -8
- data/sig/request.rbs +12 -0
- data/sig/resolver/native.rbs +6 -1
- data/sig/response.rbs +8 -3
- data/sig/selector.rbs +1 -0
- data/sig/session.rbs +2 -0
- data/sig/timers.rbs +15 -4
- data/sig/transcoder/body.rbs +1 -3
- data/sig/transcoder/json.rbs +1 -1
- data/sig/transcoder/multipart.rbs +1 -1
- data/sig/transcoder/utils/body_reader.rbs +1 -1
- data/sig/transcoder/utils/deflater.rbs +1 -2
- metadata +11 -9
- data/lib/httpx/session2.rb +0 -23
- data/lib/httpx/transcoder/utils/inflater.rb +0 -21
- data/sig/transcoder/utils/inflater.rbs +0 -12
    
        data/lib/httpx/connection.rb
    CHANGED
    
    | @@ -41,15 +41,17 @@ module HTTPX | |
| 41 41 |  | 
| 42 42 | 
             
                def_delegator :@write_buffer, :empty?
         | 
| 43 43 |  | 
| 44 | 
            -
                attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session
         | 
| 44 | 
            +
                attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session, :sibling
         | 
| 45 45 |  | 
| 46 | 
            -
                attr_writer :current_selector | 
| 46 | 
            +
                attr_writer :current_selector
         | 
| 47 47 |  | 
| 48 48 | 
             
                attr_accessor :current_session, :family
         | 
| 49 49 |  | 
| 50 | 
            +
                protected :sibling
         | 
| 51 | 
            +
             | 
| 50 52 | 
             
                def initialize(uri, options)
         | 
| 51 | 
            -
                  @current_session = @current_selector = @coalesced_connection = nil
         | 
| 52 | 
            -
                  @exhausted = @cloned = false
         | 
| 53 | 
            +
                  @current_session = @current_selector = @sibling = @coalesced_connection = nil
         | 
| 54 | 
            +
                  @exhausted = @cloned = @main_sibling = false
         | 
| 53 55 |  | 
| 54 56 | 
             
                  @options = Options.new(options)
         | 
| 55 57 | 
             
                  @type = initialize_type(uri, @options)
         | 
| @@ -70,9 +72,6 @@ module HTTPX | |
| 70 72 | 
             
                  else
         | 
| 71 73 | 
             
                    transition(:idle)
         | 
| 72 74 | 
             
                  end
         | 
| 73 | 
            -
                  on(:activate) do
         | 
| 74 | 
            -
                    @current_session.select_connection(self, @current_selector)
         | 
| 75 | 
            -
                  end
         | 
| 76 75 | 
             
                  on(:close) do
         | 
| 77 76 | 
             
                    next if @exhausted # it'll reset
         | 
| 78 77 |  | 
| @@ -86,10 +85,13 @@ module HTTPX | |
| 86 85 | 
             
                  on(:terminate) do
         | 
| 87 86 | 
             
                    next if @exhausted # it'll reset
         | 
| 88 87 |  | 
| 88 | 
            +
                    current_session = @current_session
         | 
| 89 | 
            +
                    current_selector = @current_selector
         | 
| 90 | 
            +
             | 
| 89 91 | 
             
                    # may be called after ":close" above, so after the connection has been checked back in.
         | 
| 90 | 
            -
                    next unless  | 
| 92 | 
            +
                    next unless current_session && current_selector
         | 
| 91 93 |  | 
| 92 | 
            -
                     | 
| 94 | 
            +
                    current_session.deselect_connection(self, current_selector)
         | 
| 93 95 | 
             
                  end
         | 
| 94 96 |  | 
| 95 97 | 
             
                  on(:altsvc) do |alt_origin, origin, alt_params|
         | 
| @@ -99,8 +101,6 @@ module HTTPX | |
| 99 101 | 
             
                  @inflight = 0
         | 
| 100 102 | 
             
                  @keep_alive_timeout = @options.timeout[:keep_alive_timeout]
         | 
| 101 103 |  | 
| 102 | 
            -
                  @intervals = []
         | 
| 103 | 
            -
             | 
| 104 104 | 
             
                  self.addresses = @options.addresses if @options.addresses
         | 
| 105 105 | 
             
                end
         | 
| 106 106 |  | 
| @@ -194,6 +194,12 @@ module HTTPX | |
| 194 194 | 
             
                  end
         | 
| 195 195 | 
             
                end
         | 
| 196 196 |  | 
| 197 | 
            +
                def io_connected?
         | 
| 198 | 
            +
                  return @coalesced_connection.io_connected? if @coalesced_connection
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                  @io && @io.state == :connected
         | 
| 201 | 
            +
                end
         | 
| 202 | 
            +
             | 
| 197 203 | 
             
                def connecting?
         | 
| 198 204 | 
             
                  @state == :idle
         | 
| 199 205 | 
             
                end
         | 
| @@ -290,6 +296,7 @@ module HTTPX | |
| 290 296 | 
             
                      @pending << request
         | 
| 291 297 | 
             
                      transition(:active) if @state == :inactive
         | 
| 292 298 | 
             
                      parser.ping
         | 
| 299 | 
            +
                      request.ping!
         | 
| 293 300 | 
             
                      return
         | 
| 294 301 | 
             
                    end
         | 
| 295 302 |  | 
| @@ -329,31 +336,54 @@ module HTTPX | |
| 329 336 | 
             
                end
         | 
| 330 337 |  | 
| 331 338 | 
             
                def handle_socket_timeout(interval)
         | 
| 332 | 
            -
                   | 
| 339 | 
            +
                  error = OperationTimeoutError.new(interval, "timed out while waiting on select")
         | 
| 340 | 
            +
                  error.set_backtrace(caller)
         | 
| 341 | 
            +
                  on_error(error)
         | 
| 342 | 
            +
                end
         | 
| 333 343 |  | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 344 | 
            +
                def coalesced_connection=(connection)
         | 
| 345 | 
            +
                  @coalesced_connection = connection
         | 
| 336 346 |  | 
| 337 | 
            -
             | 
| 338 | 
            -
                   | 
| 347 | 
            +
                  close_sibling
         | 
| 348 | 
            +
                  connection.merge(self)
         | 
| 349 | 
            +
                end
         | 
| 339 350 |  | 
| 340 | 
            -
             | 
| 341 | 
            -
                   | 
| 342 | 
            -
             | 
| 351 | 
            +
                def sibling=(connection)
         | 
| 352 | 
            +
                  @sibling = connection
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                  return unless connection
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                  @main_sibling = connection.sibling.nil?
         | 
| 357 | 
            +
             | 
| 358 | 
            +
                  return unless @main_sibling
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                  connection.sibling = self
         | 
| 343 361 | 
             
                end
         | 
| 344 362 |  | 
| 345 | 
            -
                 | 
| 363 | 
            +
                def handle_connect_error(error)
         | 
| 364 | 
            +
                  @connect_error = error
         | 
| 346 365 |  | 
| 347 | 
            -
             | 
| 348 | 
            -
             | 
| 366 | 
            +
                  return handle_error(error) unless @sibling && @sibling.connecting?
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                  @sibling.merge(self)
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                  force_reset(true)
         | 
| 349 371 | 
             
                end
         | 
| 350 372 |  | 
| 351 373 | 
             
                def disconnect
         | 
| 374 | 
            +
                  return unless @current_session && @current_selector
         | 
| 375 | 
            +
             | 
| 352 376 | 
             
                  emit(:close)
         | 
| 353 377 | 
             
                  @current_session = nil
         | 
| 354 378 | 
             
                  @current_selector = nil
         | 
| 355 379 | 
             
                end
         | 
| 356 380 |  | 
| 381 | 
            +
                private
         | 
| 382 | 
            +
             | 
| 383 | 
            +
                def connect
         | 
| 384 | 
            +
                  transition(:open)
         | 
| 385 | 
            +
                end
         | 
| 386 | 
            +
             | 
| 357 387 | 
             
                def consume
         | 
| 358 388 | 
             
                  return unless @io
         | 
| 359 389 |  | 
| @@ -394,6 +424,8 @@ module HTTPX | |
| 394 424 | 
             
                        siz = @io.read(@window_size, @read_buffer)
         | 
| 395 425 | 
             
                        log(level: 3, color: :cyan) { "IO READ: #{siz} bytes... (wsize: #{@window_size}, rbuffer: #{@read_buffer.bytesize})" }
         | 
| 396 426 | 
             
                        unless siz
         | 
| 427 | 
            +
                          @write_buffer.clear
         | 
| 428 | 
            +
             | 
| 397 429 | 
             
                          ex = EOFError.new("descriptor closed")
         | 
| 398 430 | 
             
                          ex.set_backtrace(caller)
         | 
| 399 431 | 
             
                          on_error(ex)
         | 
| @@ -448,6 +480,8 @@ module HTTPX | |
| 448 480 | 
             
                        end
         | 
| 449 481 | 
             
                        log(level: 3, color: :cyan) { "IO WRITE: #{siz} bytes..." }
         | 
| 450 482 | 
             
                        unless siz
         | 
| 483 | 
            +
                          @write_buffer.clear
         | 
| 484 | 
            +
             | 
| 451 485 | 
             
                          ex = EOFError.new("descriptor closed")
         | 
| 452 486 | 
             
                          ex.set_backtrace(caller)
         | 
| 453 487 | 
             
                          on_error(ex)
         | 
| @@ -531,20 +565,22 @@ module HTTPX | |
| 531 565 | 
             
                    @exhausted = true
         | 
| 532 566 | 
             
                    current_session = @current_session
         | 
| 533 567 | 
             
                    current_selector = @current_selector
         | 
| 534 | 
            -
                     | 
| 535 | 
            -
             | 
| 568 | 
            +
                    begin
         | 
| 569 | 
            +
                      parser.close
         | 
| 570 | 
            +
                      @pending.concat(parser.pending)
         | 
| 571 | 
            +
                    ensure
         | 
| 572 | 
            +
                      @current_session = current_session
         | 
| 573 | 
            +
                      @current_selector = current_selector
         | 
| 574 | 
            +
                    end
         | 
| 575 | 
            +
             | 
| 536 576 | 
             
                    case @state
         | 
| 537 577 | 
             
                    when :closed
         | 
| 538 578 | 
             
                      idling
         | 
| 539 579 | 
             
                      @exhausted = false
         | 
| 540 | 
            -
                      @current_session = current_session
         | 
| 541 | 
            -
                      @current_selector = current_selector
         | 
| 542 580 | 
             
                    when :closing
         | 
| 543 | 
            -
                      once(: | 
| 581 | 
            +
                      once(:closed) do
         | 
| 544 582 | 
             
                        idling
         | 
| 545 583 | 
             
                        @exhausted = false
         | 
| 546 | 
            -
                        @current_session = current_session
         | 
| 547 | 
            -
                        @current_selector = current_selector
         | 
| 548 584 | 
             
                      end
         | 
| 549 585 | 
             
                    end
         | 
| 550 586 | 
             
                  end
         | 
| @@ -577,9 +613,9 @@ module HTTPX | |
| 577 613 | 
             
                  parser.on(:timeout) do |tout|
         | 
| 578 614 | 
             
                    @timeout = tout
         | 
| 579 615 | 
             
                  end
         | 
| 580 | 
            -
                  parser.on(:error) do |request,  | 
| 581 | 
            -
                    case  | 
| 582 | 
            -
                    when  | 
| 616 | 
            +
                  parser.on(:error) do |request, error|
         | 
| 617 | 
            +
                    case error
         | 
| 618 | 
            +
                    when :http_1_1_required
         | 
| 583 619 | 
             
                      current_session = @current_session
         | 
| 584 620 | 
             
                      current_selector = @current_selector
         | 
| 585 621 | 
             
                      parser.close
         | 
| @@ -589,11 +625,16 @@ module HTTPX | |
| 589 625 | 
             
                      other_connection.merge(self)
         | 
| 590 626 | 
             
                      request.transition(:idle)
         | 
| 591 627 | 
             
                      other_connection.send(request)
         | 
| 592 | 
            -
             | 
| 593 | 
            -
             | 
| 594 | 
            -
                      request | 
| 595 | 
            -
                      request. | 
| 628 | 
            +
                      next
         | 
| 629 | 
            +
                    when OperationTimeoutError
         | 
| 630 | 
            +
                      # request level timeouts should take precedence
         | 
| 631 | 
            +
                      next unless request.active_timeouts.empty?
         | 
| 596 632 | 
             
                    end
         | 
| 633 | 
            +
             | 
| 634 | 
            +
                    @inflight -= 1
         | 
| 635 | 
            +
                    response = ErrorResponse.new(request, error)
         | 
| 636 | 
            +
                    request.response = response
         | 
| 637 | 
            +
                    request.emit(:response, response)
         | 
| 597 638 | 
             
                  end
         | 
| 598 639 | 
             
                end
         | 
| 599 640 |  | 
| @@ -613,14 +654,16 @@ module HTTPX | |
| 613 654 | 
             
                  # connect errors, exit gracefully
         | 
| 614 655 | 
             
                  error = ConnectionError.new(e.message)
         | 
| 615 656 | 
             
                  error.set_backtrace(e.backtrace)
         | 
| 616 | 
            -
                   | 
| 657 | 
            +
                  handle_connect_error(error) if connecting?
         | 
| 617 658 | 
             
                  @state = :closed
         | 
| 659 | 
            +
                  purge_after_closed
         | 
| 618 660 | 
             
                  disconnect
         | 
| 619 661 | 
             
                rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
         | 
| 620 662 | 
             
                  # connect errors, exit gracefully
         | 
| 621 663 | 
             
                  handle_error(e)
         | 
| 622 | 
            -
                   | 
| 664 | 
            +
                  handle_connect_error(e) if connecting?
         | 
| 623 665 | 
             
                  @state = :closed
         | 
| 666 | 
            +
                  purge_after_closed
         | 
| 624 667 | 
             
                  disconnect
         | 
| 625 668 | 
             
                end
         | 
| 626 669 |  | 
| @@ -629,12 +672,12 @@ module HTTPX | |
| 629 672 | 
             
                  when :idle
         | 
| 630 673 | 
             
                    @timeout = @current_timeout = @options.timeout[:connect_timeout]
         | 
| 631 674 |  | 
| 632 | 
            -
                    @connected_at = nil
         | 
| 675 | 
            +
                    @connected_at = @response_received_at = nil
         | 
| 633 676 | 
             
                  when :open
         | 
| 634 677 | 
             
                    return if @state == :closed
         | 
| 635 678 |  | 
| 636 679 | 
             
                    @io.connect
         | 
| 637 | 
            -
                     | 
| 680 | 
            +
                    close_sibling if @io.state == :connected
         | 
| 638 681 |  | 
| 639 682 | 
             
                    return unless @io.connected?
         | 
| 640 683 |  | 
| @@ -667,6 +710,7 @@ module HTTPX | |
| 667 710 |  | 
| 668 711 | 
             
                    purge_after_closed
         | 
| 669 712 | 
             
                    disconnect if @pending.empty?
         | 
| 713 | 
            +
             | 
| 670 714 | 
             
                  when :already_open
         | 
| 671 715 | 
             
                    nextstate = :open
         | 
| 672 716 | 
             
                    # the first check for given io readiness must still use a timeout.
         | 
| @@ -677,11 +721,29 @@ module HTTPX | |
| 677 721 | 
             
                    return unless @state == :inactive
         | 
| 678 722 |  | 
| 679 723 | 
             
                    nextstate = :open
         | 
| 680 | 
            -
             | 
| 724 | 
            +
             | 
| 725 | 
            +
                    # activate
         | 
| 726 | 
            +
                    @current_session.select_connection(self, @current_selector)
         | 
| 681 727 | 
             
                  end
         | 
| 682 728 | 
             
                  @state = nextstate
         | 
| 683 729 | 
             
                end
         | 
| 684 730 |  | 
| 731 | 
            +
                def close_sibling
         | 
| 732 | 
            +
                  return unless @sibling
         | 
| 733 | 
            +
             | 
| 734 | 
            +
                  if @sibling.io_connected?
         | 
| 735 | 
            +
                    reset
         | 
| 736 | 
            +
                    # TODO: transition connection to closed
         | 
| 737 | 
            +
                  end
         | 
| 738 | 
            +
             | 
| 739 | 
            +
                  unless @sibling.state == :closed
         | 
| 740 | 
            +
                    merge(@sibling) unless @main_sibling
         | 
| 741 | 
            +
                    @sibling.force_reset(true)
         | 
| 742 | 
            +
                  end
         | 
| 743 | 
            +
             | 
| 744 | 
            +
                  @sibling = nil
         | 
| 745 | 
            +
                end
         | 
| 746 | 
            +
             | 
| 685 747 | 
             
                def purge_after_closed
         | 
| 686 748 | 
             
                  @io.close if @io
         | 
| 687 749 | 
             
                  @read_buffer.clear
         | 
| @@ -754,7 +816,7 @@ module HTTPX | |
| 754 816 | 
             
                end
         | 
| 755 817 |  | 
| 756 818 | 
             
                def on_error(error, request = nil)
         | 
| 757 | 
            -
                  if error. | 
| 819 | 
            +
                  if error.is_a?(OperationTimeoutError)
         | 
| 758 820 |  | 
| 759 821 | 
             
                    # inactive connections do not contribute to the select loop, therefore
         | 
| 760 822 | 
             
                    # they should not fail due to such errors.
         | 
| @@ -783,6 +845,7 @@ module HTTPX | |
| 783 845 |  | 
| 784 846 | 
             
                  return unless request
         | 
| 785 847 |  | 
| 848 | 
            +
                  @inflight -= 1
         | 
| 786 849 | 
             
                  response = ErrorResponse.new(request, error)
         | 
| 787 850 | 
             
                  request.response = response
         | 
| 788 851 | 
             
                  request.emit(:response, response)
         | 
| @@ -799,7 +862,7 @@ module HTTPX | |
| 799 862 |  | 
| 800 863 | 
             
                  return if read_timeout.nil? || read_timeout.infinite?
         | 
| 801 864 |  | 
| 802 | 
            -
                  set_request_timeout(request, read_timeout, :done, :response) do
         | 
| 865 | 
            +
                  set_request_timeout(:read_timeout, request, read_timeout, :done, :response) do
         | 
| 803 866 | 
             
                    read_timeout_callback(request, read_timeout)
         | 
| 804 867 | 
             
                  end
         | 
| 805 868 | 
             
                end
         | 
| @@ -809,7 +872,7 @@ module HTTPX | |
| 809 872 |  | 
| 810 873 | 
             
                  return if write_timeout.nil? || write_timeout.infinite?
         | 
| 811 874 |  | 
| 812 | 
            -
                  set_request_timeout(request, write_timeout, :headers, %i[done response]) do
         | 
| 875 | 
            +
                  set_request_timeout(:write_timeout, request, write_timeout, :headers, %i[done response]) do
         | 
| 813 876 | 
             
                    write_timeout_callback(request, write_timeout)
         | 
| 814 877 | 
             
                  end
         | 
| 815 878 | 
             
                end
         | 
| @@ -819,7 +882,7 @@ module HTTPX | |
| 819 882 |  | 
| 820 883 | 
             
                  return if request_timeout.nil? || request_timeout.infinite?
         | 
| 821 884 |  | 
| 822 | 
            -
                  set_request_timeout(request, request_timeout, :headers, :complete) do
         | 
| 885 | 
            +
                  set_request_timeout(:request_timeout, request, request_timeout, :headers, :complete) do
         | 
| 823 886 | 
             
                    read_timeout_callback(request, request_timeout, RequestTimeoutError)
         | 
| 824 887 | 
             
                  end
         | 
| 825 888 | 
             
                end
         | 
| @@ -844,21 +907,18 @@ module HTTPX | |
| 844 907 | 
             
                  on_error(error, request)
         | 
| 845 908 | 
             
                end
         | 
| 846 909 |  | 
| 847 | 
            -
                def set_request_timeout(request, timeout, start_event, finish_events, &callback)
         | 
| 848 | 
            -
                  request. | 
| 849 | 
            -
                     | 
| 910 | 
            +
                def set_request_timeout(label, request, timeout, start_event, finish_events, &callback)
         | 
| 911 | 
            +
                  request.set_timeout_callback(start_event) do
         | 
| 912 | 
            +
                    timer = @current_selector.after(timeout, callback)
         | 
| 913 | 
            +
                    request.active_timeouts << label
         | 
| 850 914 |  | 
| 851 915 | 
             
                    Array(finish_events).each do |event|
         | 
| 852 916 | 
             
                      # clean up request timeouts if the connection errors out
         | 
| 853 | 
            -
                      request. | 
| 854 | 
            -
                         | 
| 855 | 
            -
             | 
| 856 | 
            -
                          @intervals.delete(interval) if interval.no_callbacks?
         | 
| 857 | 
            -
                        end
         | 
| 917 | 
            +
                      request.set_timeout_callback(event) do
         | 
| 918 | 
            +
                        timer.cancel
         | 
| 919 | 
            +
                        request.active_timeouts.delete(label)
         | 
| 858 920 | 
             
                      end
         | 
| 859 921 | 
             
                    end
         | 
| 860 | 
            -
             | 
| 861 | 
            -
                    @intervals << interval
         | 
| 862 922 | 
             
                  end
         | 
| 863 923 | 
             
                end
         | 
| 864 924 |  | 
    
        data/lib/httpx/errors.rb
    CHANGED
    
    | @@ -77,6 +77,9 @@ module HTTPX | |
| 77 77 | 
             
              # Error raised when there was a timeout while resolving a domain to an IP.
         | 
| 78 78 | 
             
              class ResolveTimeoutError < TimeoutError; end
         | 
| 79 79 |  | 
| 80 | 
            +
              # Error raise when there was a timeout waiting for readiness of the socket the request is related to.
         | 
| 81 | 
            +
              class OperationTimeoutError < TimeoutError; end
         | 
| 82 | 
            +
             | 
| 80 83 | 
             
              # Error raised when there was an error while resolving a domain to an IP.
         | 
| 81 84 | 
             
              class ResolveError < Error; end
         | 
| 82 85 |  | 
| @@ -112,8 +115,4 @@ module HTTPX | |
| 112 115 | 
             
                  @response.status
         | 
| 113 116 | 
             
                end
         | 
| 114 117 | 
             
              end
         | 
| 115 | 
            -
             | 
| 116 | 
            -
              # error raised when a request was sent a server which can't reproduce a response, and
         | 
| 117 | 
            -
              # has therefore returned an HTTP response using the 421 status code.
         | 
| 118 | 
            -
              class MisdirectedRequestError < HTTPError; end
         | 
| 119 118 | 
             
            end
         | 
    
        data/lib/httpx/io/ssl.rb
    CHANGED
    
    | @@ -92,9 +92,12 @@ module HTTPX | |
| 92 92 | 
             
                end
         | 
| 93 93 |  | 
| 94 94 | 
             
                def connect
         | 
| 95 | 
            -
                   | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 95 | 
            +
                  return if @state == :negotiated
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  unless @state == :connected
         | 
| 98 | 
            +
                    super
         | 
| 99 | 
            +
                    return unless @state == :connected
         | 
| 100 | 
            +
                  end
         | 
| 98 101 |  | 
| 99 102 | 
             
                  unless @io.is_a?(OpenSSL::SSL::SSLSocket)
         | 
| 100 103 | 
             
                    if (hostname_is_ip = (@ip == @sni_hostname))
         | 
    
        data/lib/httpx/loggable.rb
    CHANGED
    
    | @@ -15,20 +15,27 @@ module HTTPX | |
| 15 15 |  | 
| 16 16 | 
             
                USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
         | 
| 17 17 |  | 
| 18 | 
            -
                def log(level: @options.debug_level, color: nil, &msg)
         | 
| 19 | 
            -
                  return unless  | 
| 18 | 
            +
                def log(level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug, &msg)
         | 
| 19 | 
            +
                  return unless debug_level >= level
         | 
| 20 20 |  | 
| 21 | 
            -
                  debug_stream =  | 
| 21 | 
            +
                  debug_stream = debug || ($stderr if USE_DEBUG_LOG)
         | 
| 22 22 |  | 
| 23 23 | 
             
                  return unless debug_stream
         | 
| 24 24 |  | 
| 25 | 
            -
                   | 
| 25 | 
            +
                  klass = self.class
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  until (class_name = klass.name)
         | 
| 28 | 
            +
                    klass = klass.superclass
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  message = +"(pid:#{Process.pid} tid:#{Thread.current.object_id}, self:#{class_name}##{object_id}) "
         | 
| 32 | 
            +
                  message << msg.call << "\n"
         | 
| 26 33 | 
             
                  message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
         | 
| 27 34 | 
             
                  debug_stream << message
         | 
| 28 35 | 
             
                end
         | 
| 29 36 |  | 
| 30 | 
            -
                def log_exception(ex, level: @options.debug_level, color: nil)
         | 
| 31 | 
            -
                  log(level: level, color: color) { ex.full_message }
         | 
| 37 | 
            +
                def log_exception(ex, level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug)
         | 
| 38 | 
            +
                  log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
         | 
| 32 39 | 
             
                end
         | 
| 33 40 | 
             
              end
         | 
| 34 41 | 
             
            end
         | 
    
        data/lib/httpx/plugins/expect.rb
    CHANGED
    
    | @@ -84,7 +84,7 @@ module HTTPX | |
| 84 84 |  | 
| 85 85 | 
             
                      return if expect_timeout.nil? || expect_timeout.infinite?
         | 
| 86 86 |  | 
| 87 | 
            -
                      set_request_timeout(request, expect_timeout, :expect, %i[body response]) do
         | 
| 87 | 
            +
                      set_request_timeout(:expect_timeout, request, expect_timeout, :expect, %i[body response]) do
         | 
| 88 88 | 
             
                        # expect timeout expired
         | 
| 89 89 | 
             
                        if request.state == :expect && !request.expects?
         | 
| 90 90 | 
             
                          Expect.no_expect_store << request.origin
         | 
| @@ -13,6 +13,12 @@ module HTTPX | |
| 13 13 | 
             
                # by the end user in $http_init_time, different diff metrics can be shown. The "point of time" is calculated
         | 
| 14 14 | 
             
                # using the monotonic clock.
         | 
| 15 15 | 
             
                module InternalTelemetry
         | 
| 16 | 
            +
                  DEBUG_LEVEL = 3
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def self.extra_options(options)
         | 
| 19 | 
            +
                    options.merge(debug_level: 3)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 16 22 | 
             
                  module TrackTimeMethods
         | 
| 17 23 | 
             
                    private
         | 
| 18 24 |  | 
| @@ -28,7 +34,19 @@ module HTTPX | |
| 28 34 | 
             
                      after_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
         | 
| 29 35 | 
             
                      # $http_init_time = after_time
         | 
| 30 36 | 
             
                      elapsed = after_time - prev_time
         | 
| 31 | 
            -
                       | 
| 37 | 
            +
                      # klass = self.class
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                      # until (class_name = klass.name)
         | 
| 40 | 
            +
                      #   klass = klass.superclass
         | 
| 41 | 
            +
                      # end
         | 
| 42 | 
            +
                      log(
         | 
| 43 | 
            +
                        level: DEBUG_LEVEL,
         | 
| 44 | 
            +
                        color: :red,
         | 
| 45 | 
            +
                        debug_level: @options ? @options.debug_level : DEBUG_LEVEL,
         | 
| 46 | 
            +
                        debug: nil
         | 
| 47 | 
            +
                      ) do
         | 
| 48 | 
            +
                        "[ELAPSED TIME]: #{label}: #{elapsed} (ms)" << "\e[0m"
         | 
| 49 | 
            +
                      end
         | 
| 32 50 | 
             
                    end
         | 
| 33 51 | 
             
                  end
         | 
| 34 52 |  | 
| @@ -88,6 +106,7 @@ module HTTPX | |
| 88 106 |  | 
| 89 107 | 
             
                  module RequestMethods
         | 
| 90 108 | 
             
                    def self.included(klass)
         | 
| 109 | 
            +
                      klass.prepend Loggable
         | 
| 91 110 | 
             
                      klass.prepend TrackTimeMethods
         | 
| 92 111 | 
             
                      super
         | 
| 93 112 | 
             
                    end
         | 
| @@ -114,6 +133,7 @@ module HTTPX | |
| 114 133 |  | 
| 115 134 | 
             
                  module PoolMethods
         | 
| 116 135 | 
             
                    def self.included(klass)
         | 
| 136 | 
            +
                      klass.prepend Loggable
         | 
| 117 137 | 
             
                      klass.prepend TrackTimeMethods
         | 
| 118 138 | 
             
                      super
         | 
| 119 139 | 
             
                    end
         | 
| @@ -110,7 +110,7 @@ module HTTPX | |
| 110 110 | 
             
                         )
         | 
| 111 111 | 
             
                        __try_partial_retry(request, response)
         | 
| 112 112 | 
             
                        log { "failed to get response, #{request.retries} tries to go..." }
         | 
| 113 | 
            -
                        request.retries -= 1
         | 
| 113 | 
            +
                        request.retries -= 1 unless request.ping? # do not exhaust retries on connection liveness probes
         | 
| 114 114 | 
             
                        request.transition(:idle)
         | 
| 115 115 |  | 
| 116 116 | 
             
                        retry_after = options.retry_after
         | 
| @@ -167,7 +167,7 @@ module HTTPX | |
| 167 167 | 
             
                      unless response.headers.key?("accept-ranges") &&
         | 
| 168 168 | 
             
                             response.headers["accept-ranges"] == "bytes" && # there's nothing else supported though...
         | 
| 169 169 | 
             
                             (original_body = response.body)
         | 
| 170 | 
            -
                        response. | 
| 170 | 
            +
                        response.body.close
         | 
| 171 171 | 
             
                        return
         | 
| 172 172 | 
             
                      end
         | 
| 173 173 |  | 
    
        data/lib/httpx/plugins/stream.rb
    CHANGED
    
    | @@ -4,27 +4,39 @@ module HTTPX | |
| 4 4 | 
             
              class StreamResponse
         | 
| 5 5 | 
             
                def initialize(request, session)
         | 
| 6 6 | 
             
                  @request = request
         | 
| 7 | 
            +
                  @options = @request.options
         | 
| 7 8 | 
             
                  @session = session
         | 
| 8 | 
            -
                  @ | 
| 9 | 
            +
                  @response_enum = nil
         | 
| 10 | 
            +
                  @buffered_chunks = []
         | 
| 9 11 | 
             
                end
         | 
| 10 12 |  | 
| 11 13 | 
             
                def each(&block)
         | 
| 12 14 | 
             
                  return enum_for(__method__) unless block
         | 
| 13 15 |  | 
| 16 | 
            +
                  if (response_enum = @response_enum)
         | 
| 17 | 
            +
                    @response_enum = nil
         | 
| 18 | 
            +
                    # streaming already started, let's finish it
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    while (chunk = @buffered_chunks.shift)
         | 
| 21 | 
            +
                      block.call(chunk)
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    # consume enum til the end
         | 
| 25 | 
            +
                    begin
         | 
| 26 | 
            +
                      while (chunk = response_enum.next)
         | 
| 27 | 
            +
                        block.call(chunk)
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
                    rescue StopIteration
         | 
| 30 | 
            +
                      return
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 14 34 | 
             
                  @request.stream = self
         | 
| 15 35 |  | 
| 16 36 | 
             
                  begin
         | 
| 17 37 | 
             
                    @on_chunk = block
         | 
| 18 38 |  | 
| 19 | 
            -
                     | 
| 20 | 
            -
                      # if we've already started collecting the payload, yield it first
         | 
| 21 | 
            -
                      # before proceeding.
         | 
| 22 | 
            -
                      body = @request.response.body
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                      body.each do |chunk|
         | 
| 25 | 
            -
                        on_chunk(chunk)
         | 
| 26 | 
            -
                      end
         | 
| 27 | 
            -
                    end
         | 
| 39 | 
            +
                    response = @session.request(@request)
         | 
| 28 40 |  | 
| 29 41 | 
             
                    response.raise_for_status
         | 
| 30 42 | 
             
                  ensure
         | 
| @@ -64,27 +76,39 @@ module HTTPX | |
| 64 76 | 
             
                # :nocov:
         | 
| 65 77 |  | 
| 66 78 | 
             
                def to_s
         | 
| 67 | 
            -
                  response | 
| 79 | 
            +
                  if @request.response
         | 
| 80 | 
            +
                    @request.response.to_s
         | 
| 81 | 
            +
                  else
         | 
| 82 | 
            +
                    @buffered_chunks.join
         | 
| 83 | 
            +
                  end
         | 
| 68 84 | 
             
                end
         | 
| 69 85 |  | 
| 70 86 | 
             
                private
         | 
| 71 87 |  | 
| 72 88 | 
             
                def response
         | 
| 73 | 
            -
                  return @response if @response
         | 
| 74 | 
            -
             | 
| 75 89 | 
             
                  @request.response || begin
         | 
| 76 | 
            -
                     | 
| 90 | 
            +
                    response_enum = each
         | 
| 91 | 
            +
                    while (chunk = response_enum.next)
         | 
| 92 | 
            +
                      @buffered_chunks << chunk
         | 
| 93 | 
            +
                      break if @request.response
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                    @response_enum = response_enum
         | 
| 96 | 
            +
                    @request.response
         | 
| 77 97 | 
             
                  end
         | 
| 78 98 | 
             
                end
         | 
| 79 99 |  | 
| 80 | 
            -
                def respond_to_missing?(meth,  | 
| 81 | 
            -
                  response. | 
| 100 | 
            +
                def respond_to_missing?(meth, include_private)
         | 
| 101 | 
            +
                  if (response = @request.response)
         | 
| 102 | 
            +
                    response.respond_to_missing?(meth, include_private)
         | 
| 103 | 
            +
                  else
         | 
| 104 | 
            +
                    @options.response_class.method_defined?(meth) || (include_private && @options.response_class.private_method_defined?(meth))
         | 
| 105 | 
            +
                  end || super
         | 
| 82 106 | 
             
                end
         | 
| 83 107 |  | 
| 84 | 
            -
                def method_missing(meth, *args, &block)
         | 
| 108 | 
            +
                def method_missing(meth, *args, **kwargs, &block)
         | 
| 85 109 | 
             
                  return super unless response.respond_to?(meth)
         | 
| 86 110 |  | 
| 87 | 
            -
                  response.__send__(meth, *args, &block)
         | 
| 111 | 
            +
                  response.__send__(meth, *args, **kwargs, &block)
         | 
| 88 112 | 
             
                end
         | 
| 89 113 | 
             
              end
         | 
| 90 114 |  | 
    
        data/lib/httpx/request/body.rb
    CHANGED
    
    | @@ -52,7 +52,11 @@ module HTTPX | |
| 52 52 |  | 
| 53 53 | 
             
                  body = stream(@body)
         | 
| 54 54 | 
             
                  if body.respond_to?(:read)
         | 
| 55 | 
            -
                     | 
| 55 | 
            +
                    while (chunk = body.read(16_384))
         | 
| 56 | 
            +
                      block.call(chunk)
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                    # TODO: use copy_stream once bug is resolved: https://bugs.ruby-lang.org/issues/21131
         | 
| 59 | 
            +
                    # ::IO.copy_stream(body, ProcIO.new(block))
         | 
| 56 60 | 
             
                  elsif body.respond_to?(:each)
         | 
| 57 61 | 
             
                    body.each(&block)
         | 
| 58 62 | 
             
                  else
         | 
| @@ -60,6 +64,10 @@ module HTTPX | |
| 60 64 | 
             
                  end
         | 
| 61 65 | 
             
                end
         | 
| 62 66 |  | 
| 67 | 
            +
                def close
         | 
| 68 | 
            +
                  @body.close if @body.respond_to?(:close)
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 63 71 | 
             
                # if the +@body+ is rewindable, it rewinnds it.
         | 
| 64 72 | 
             
                def rewind
         | 
| 65 73 | 
             
                  return if empty?
         | 
| @@ -142,17 +150,4 @@ module HTTPX | |
| 142 150 | 
             
                  end
         | 
| 143 151 | 
             
                end
         | 
| 144 152 | 
             
              end
         | 
| 145 | 
            -
             | 
| 146 | 
            -
              # Wrapper yielder which can be used with functions which expect an IO writer.
         | 
| 147 | 
            -
              class ProcIO
         | 
| 148 | 
            -
                def initialize(block)
         | 
| 149 | 
            -
                  @block = block
         | 
| 150 | 
            -
                end
         | 
| 151 | 
            -
             | 
| 152 | 
            -
                # Implementation the IO write protocol, which yield the given chunk to +@block+.
         | 
| 153 | 
            -
                def write(data)
         | 
| 154 | 
            -
                  @block.call(data.dup)
         | 
| 155 | 
            -
                  data.bytesize
         | 
| 156 | 
            -
                end
         | 
| 157 | 
            -
              end
         | 
| 158 153 | 
             
            end
         |