diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 92f3cd73..8b03267e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,14 +20,20 @@ test_ruby21: script: ./spec.sh ruby 2.1 test_ruby22: + only: + - master stage: test script: ./spec.sh ruby 2.2 test_ruby23: + only: + - master stage: test script: ./spec.sh ruby 2.3 test_ruby24: + only: + - master stage: test script: ./spec.sh ruby 2.4 diff --git a/Gemfile b/Gemfile index 4ca510e7..b9f7ab59 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gemspec gem "hanna-nouveau", require: false gem "rake", "~> 12.3" -gem "rubocop", require: false +gem "rubocop", "~> 0.52.1", require: false gem "simplecov", require: false platform :mri do diff --git a/README.md b/README.md index 64b27b83..620dddd7 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,10 @@ It means that it loads the bare minimum to perform requests, and the user has to It also means that it ships with the minimum amount of dependencies. +## Easy to test + +The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/httpbin/), so there is no mocking/stubbing going on. The test suite uses [minitest](https://github.com/seattlerb/minitest), but its matchers usage is limit to assert (assert is all you need). + ## Supported Rubies All Rubies greater or equal to 2.1, and always latest JRuby. diff --git a/lib/httpx/channel.rb b/lib/httpx/channel.rb index 8bee7e62..d6ef3856 100644 --- a/lib/httpx/channel.rb +++ b/lib/httpx/channel.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "resolv" require "forwardable" require "httpx/io" require "httpx/buffer" @@ -68,13 +69,13 @@ module HTTPX end def match?(uri) - ip = begin - TCPSocket.getaddress(uri.host) - rescue StandardError - uri.host - end + ips = begin + Resolv.getaddresses(uri.host) + rescue StandardError + [uri.host] + end - ip == @io.ip && + ips.include?(@io.ip) && uri.port == @io.port && uri.scheme == @io.scheme end @@ -98,18 +99,9 @@ module HTTPX @io.to_io end - def close(hard = false) - pr = @parser + def close + @parser.close transition(:closing) - if hard || (pr && pr.empty?) - pr.close - @parser = nil - else - transition(:idle) - @parser = pr - parser.reenqueue! - return - end end def reset @@ -154,7 +146,10 @@ module HTTPX def dread(wsize = @window_size) loop do siz = @io.read(wsize, @read_buffer) - throw(:close, self) unless siz + unless siz + emit(:close) + return + end return if siz.zero? log { "READ: #{siz} bytes..." } parser << @read_buffer.to_s @@ -165,7 +160,10 @@ module HTTPX loop do return if @write_buffer.empty? siz = @io.write(@write_buffer) - throw(:close, self) unless siz + unless siz + emit(:close) + return + end log { "WRITE: #{siz} bytes..." } return if siz.zero? end @@ -190,11 +188,8 @@ module HTTPX parser.on(:promise) do |*args| emit(:promise, *args) end - # parser.inherit_callbacks(self) - parser.on(:complete) { throw(:close, self) } parser.on(:close) do - transition(:closed) - emit(:close) + transition(:closing) end parser end diff --git a/lib/httpx/channel/http1.rb b/lib/httpx/channel/http1.rb index d1ee5742..ee3c9591 100644 --- a/lib/httpx/channel/http1.rb +++ b/lib/httpx/channel/http1.rb @@ -151,7 +151,7 @@ module HTTPX # 1 keep alive request. @max_concurrent_requests = 1 end - emit(:complete) + emit(:close) end private diff --git a/lib/httpx/channel/http2.rb b/lib/httpx/channel/http2.rb index 96e97b1d..680444da 100644 --- a/lib/httpx/channel/http2.rb +++ b/lib/httpx/channel/http2.rb @@ -170,7 +170,7 @@ module HTTPX def on_close(*) return unless @connection.state == :closed && @connection.active_stream_count.zero? - emit(:complete) + emit(:close) end def on_frame_sent(frame) diff --git a/lib/httpx/connection.rb b/lib/httpx/connection.rb index a58eadfd..54ead50d 100644 --- a/lib/httpx/connection.rb +++ b/lib/httpx/connection.rb @@ -19,19 +19,15 @@ module HTTPX def next_tick(timeout: @timeout.timeout) @selector.select(timeout) do |monitor| if (channel = monitor.value) - consume(channel) + channel.call end monitor.interests = channel.interests end end - def close(channel = nil) - if channel - channel.close - else - @channels.each(&:close) - next_tick until @selector.empty? - end + def close + @channels.each(&:close) + next_tick until @channels.empty? end def reset @@ -65,10 +61,5 @@ module HTTPX end @channels << channel end - - def consume(channel) - ch = catch(:close) { channel.call } - close(ch) if ch - end end end diff --git a/lib/httpx/io.rb b/lib/httpx/io.rb index 7c63c125..24243495 100644 --- a/lib/httpx/io.rb +++ b/lib/httpx/io.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "resolv" require "socket" require "openssl" require "ipaddr" @@ -19,7 +20,7 @@ module HTTPX if @options.io @io = case @options.io when Hash - @ip = TCPSocket.getaddress(hostname) + @ip = Resolv.getaddress(@hostname) @options.io[@ip] || @options.io["#{@ip}:#{@port}"] else @ip = hostname @@ -30,7 +31,7 @@ module HTTPX @state = :connected end else - @ip = TCPSocket.getaddress(hostname) + @ip = Resolv.getaddress(@hostname) end @io ||= build_socket end diff --git a/lib/httpx/plugins/proxy/http.rb b/lib/httpx/plugins/proxy/http.rb index b5a1f97a..3bdcd0c3 100644 --- a/lib/httpx/plugins/proxy/http.rb +++ b/lib/httpx/plugins/proxy/http.rb @@ -34,7 +34,7 @@ module HTTPX return if @io.closed? @parser = ConnectProxyParser.new(@write_buffer, @options.merge(max_concurrent_requests: 1)) @parser.once(:response, &method(:on_connect)) - @parser.on(:complete) { throw(:close, self) } + @parser.on(:close) { transition(:closing) } proxy_connect return if @state == :open when :open @@ -45,7 +45,7 @@ module HTTPX when :idle @parser = ProxyParser.new(@write_buffer, @options) @parser.inherit_callbacks(self) - @parser.on(:complete) { throw(:close, self) } + @parser.on(:close) { transition(:closing) } end end super diff --git a/lib/httpx/selector.rb b/lib/httpx/selector.rb index 79c87b31..8eeb3e69 100644 --- a/lib/httpx/selector.rb +++ b/lib/httpx/selector.rb @@ -48,10 +48,6 @@ class HTTPX::Selector @closed = false end - def empty? - @readers.empty? && @writers.empty? - end - # deregisters +io+ from selectables. def deregister(io) @lock.synchronize do