diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18335d1e..ef01c905 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - ruby: [ ruby-2.4, ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0, jruby-9.2 ] + ruby: [ ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0 ] os: [ ubuntu-latest ] steps: @@ -56,7 +56,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: - ruby-version: 2.4 + ruby-version: 2.5 bundler-cache: true - name: bundle exec rubocop diff --git a/.rubocop.yml b/.rubocop.yml index 2dadbaed..e27f2a20 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,4 +7,4 @@ AllCops: DefaultFormatter: fuubar DisplayCopNames: true NewCops: enable - TargetRubyVersion: 2.4 + TargetRubyVersion: 2.5 diff --git a/README.md b/README.md index 4788c5d3..8a4e0c25 100644 --- a/README.md +++ b/README.md @@ -165,8 +165,6 @@ and call `#readpartial` on it repeatedly until it returns `nil`: This library aims to support and is [tested against][travis] the following Ruby versions: -* Ruby 2.4 -* Ruby 2.5 * Ruby 2.6 * Ruby 2.7 * Ruby 3.0 diff --git a/http.gemspec b/http.gemspec index bd245ae1..99f8b5bf 100644 --- a/http.gemspec +++ b/http.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |gem| gem.require_paths = ["lib"] gem.version = HTTP::VERSION - gem.required_ruby_version = ">= 2.4" + gem.required_ruby_version = ">= 2.5" gem.add_runtime_dependency "addressable", "~> 2.3" gem.add_runtime_dependency "http-cookie", "~> 1.0" diff --git a/lib/http/headers.rb b/lib/http/headers.rb index aad91ea3..7a81814c 100644 --- a/lib/http/headers.rb +++ b/lib/http/headers.rb @@ -111,7 +111,7 @@ def include?(name) # # @return [Hash] def to_h - Hash[keys.map { |k| [k, self[k]] }] + keys.map { |k| [k, self[k]] }.to_h end alias to_hash to_h diff --git a/lib/http/response/status.rb b/lib/http/response/status.rb index 2682d512..8b155a37 100644 --- a/lib/http/response/status.rb +++ b/lib/http/response/status.rb @@ -69,7 +69,7 @@ def symbolize(str) # SYMBOL_CODES[:im_a_teapot] # => 418 # # @return [Hash Fixnum>] - SYMBOL_CODES = Hash[SYMBOLS.map { |k, v| [v, k] }].freeze + SYMBOL_CODES = SYMBOLS.map { |k, v| [v, k] }.to_h.freeze # @return [Fixnum] status code attr_reader :code diff --git a/lib/http/timeout/global.rb b/lib/http/timeout/global.rb index 1078d889..857ec9a1 100644 --- a/lib/http/timeout/global.rb +++ b/lib/http/timeout/global.rb @@ -59,22 +59,12 @@ def write(data) private - if RUBY_VERSION < "2.1.0" - def read_nonblock(size, buffer = nil) - @socket.read_nonblock(size, buffer) - end - - def write_nonblock(data) - @socket.write_nonblock(data) - end - else - def read_nonblock(size, buffer = nil) - @socket.read_nonblock(size, buffer, :exception => false) - end + def read_nonblock(size, buffer = nil) + @socket.read_nonblock(size, buffer, :exception => false) + end - def write_nonblock(data) - @socket.write_nonblock(data, :exception => false) - end + def write_nonblock(data) + @socket.write_nonblock(data, :exception => false) end # Perform the given I/O operation with the given argument @@ -82,20 +72,18 @@ def perform_io reset_timer loop do - begin - result = yield - - case result - when :wait_readable then wait_readable_or_timeout - when :wait_writable then wait_writable_or_timeout - when NilClass then return :eof - else return result - end - rescue IO::WaitReadable - wait_readable_or_timeout - rescue IO::WaitWritable - wait_writable_or_timeout + result = yield + + case result + when :wait_readable then wait_readable_or_timeout + when :wait_writable then wait_writable_or_timeout + when NilClass then return :eof + else return result end + rescue IO::WaitReadable + wait_readable_or_timeout + rescue IO::WaitWritable + wait_writable_or_timeout end rescue EOFError :eof diff --git a/lib/http/timeout/per_operation.rb b/lib/http/timeout/per_operation.rb index a5e594fb..bc171ee0 100644 --- a/lib/http/timeout/per_operation.rb +++ b/lib/http/timeout/per_operation.rb @@ -34,66 +34,42 @@ def connect_ssl end end - # NIO with exceptions - if RUBY_VERSION < "2.1.0" - # Read data from the socket - def readpartial(size, buffer = nil) - rescue_readable do - @socket.read_nonblock(size, buffer) - end - rescue EOFError - :eof - end - - # Write data to the socket - def write(data) - rescue_writable do - @socket.write_nonblock(data) - end - rescue EOFError - :eof - end - - # NIO without exceptions - else - # Read data from the socket - def readpartial(size, buffer = nil) - timeout = false - loop do - result = @socket.read_nonblock(size, buffer, :exception => false) - - return :eof if result.nil? - return result if result != :wait_readable - - raise TimeoutError, "Read timed out after #{@read_timeout} seconds" if timeout - - # marking the socket for timeout. Why is this not being raised immediately? - # it seems there is some race-condition on the network level between calling - # #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting - # for reads, and when waiting for x seconds, it returns nil suddenly without completing - # the x seconds. In a normal case this would be a timeout on wait/read, but it can - # also mean that the socket has been closed by the server. Therefore we "mark" the - # socket for timeout and try to read more bytes. If it returns :eof, it's all good, no - # timeout. Else, the first timeout was a proper timeout. - # This hack has to be done because io/wait#wait_readable doesn't provide a value for when - # the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks. - timeout = true unless @socket.to_io.wait_readable(@read_timeout) - end + # Read data from the socket + def readpartial(size, buffer = nil) + timeout = false + loop do + result = @socket.read_nonblock(size, buffer, :exception => false) + + return :eof if result.nil? + return result if result != :wait_readable + + raise TimeoutError, "Read timed out after #{@read_timeout} seconds" if timeout + + # marking the socket for timeout. Why is this not being raised immediately? + # it seems there is some race-condition on the network level between calling + # #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting + # for reads, and when waiting for x seconds, it returns nil suddenly without completing + # the x seconds. In a normal case this would be a timeout on wait/read, but it can + # also mean that the socket has been closed by the server. Therefore we "mark" the + # socket for timeout and try to read more bytes. If it returns :eof, it's all good, no + # timeout. Else, the first timeout was a proper timeout. + # This hack has to be done because io/wait#wait_readable doesn't provide a value for when + # the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks. + timeout = true unless @socket.to_io.wait_readable(@read_timeout) end + end - # Write data to the socket - def write(data) - timeout = false - loop do - result = @socket.write_nonblock(data, :exception => false) - return result unless result == :wait_writable + # Write data to the socket + def write(data) + timeout = false + loop do + result = @socket.write_nonblock(data, :exception => false) + return result unless result == :wait_writable - raise TimeoutError, "Write timed out after #{@write_timeout} seconds" if timeout + raise TimeoutError, "Write timed out after #{@write_timeout} seconds" if timeout - timeout = true unless @socket.to_io.wait_writable(@write_timeout) - end + timeout = true unless @socket.to_io.wait_writable(@write_timeout) end - end end end diff --git a/spec/lib/http/client_spec.rb b/spec/lib/http/client_spec.rb index 0aef24d0..d6bba85d 100644 --- a/spec/lib/http/client_spec.rb +++ b/spec/lib/http/client_spec.rb @@ -20,8 +20,8 @@ def stubs end def stub(stubs) - @stubs = stubs.each_with_object({}) do |(k, v), o| - o[HTTP::URI::NORMALIZER.call(k).to_s] = v + @stubs = stubs.transform_keys do |k| + HTTP::URI::NORMALIZER.call(k).to_s end self