From 2557eae5f76dee988160085d3e09d39288782818 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 00:41:16 +0000 Subject: [PATCH 1/9] connection close: basing on channels to know whether to execute next tick --- lib/httpx/connection.rb | 17 ++++------------- lib/httpx/selector.rb | 4 ---- 2 files changed, 4 insertions(+), 17 deletions(-) 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/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 From 7db7fad7a9d0cc2dc6240609970f4428fe5284e3 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 00:41:43 +0000 Subject: [PATCH 2/9] parsers now emitting close, not complete --- lib/httpx/channel/http1.rb | 2 +- lib/httpx/channel/http2.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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) From 0763242cce508bd7daeb5037b414d110bde31c10 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 00:43:13 +0000 Subject: [PATCH 3/9] do not throw stuff, parser on close must trigger transition --- lib/httpx/channel.rb | 15 +++++++++------ lib/httpx/plugins/proxy/http.rb | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/httpx/channel.rb b/lib/httpx/channel.rb index 8bee7e62..0caac896 100644 --- a/lib/httpx/channel.rb +++ b/lib/httpx/channel.rb @@ -154,7 +154,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 +168,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 +196,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/plugins/proxy/http.rb b/lib/httpx/plugins/proxy/http.rb index b5a1f97a..7b7cd41f 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 From 9ff6db30c690c9b827afd48681d49282a885130f Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 00:43:41 +0000 Subject: [PATCH 4/9] not making too much stuff up when closing a channel --- lib/httpx/channel.rb | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/httpx/channel.rb b/lib/httpx/channel.rb index 0caac896..fc08ab01 100644 --- a/lib/httpx/channel.rb +++ b/lib/httpx/channel.rb @@ -98,18 +98,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 From 05e4f103b36e1807790db102b7653c078c6377e6 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 01:02:37 +0000 Subject: [PATCH 5/9] fixing rubocop (sort of) --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0df3803f841948d1507ce9b2bc58133a5025eb11 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 01:02:56 +0000 Subject: [PATCH 6/9] missing complete callback --- lib/httpx/plugins/proxy/http.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/httpx/plugins/proxy/http.rb b/lib/httpx/plugins/proxy/http.rb index 7b7cd41f..3bdcd0c3 100644 --- a/lib/httpx/plugins/proxy/http.rb +++ b/lib/httpx/plugins/proxy/http.rb @@ -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 From 278c5a4e260f0d211f29c3282f5ffed93329b842 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sun, 11 Mar 2018 01:03:13 +0000 Subject: [PATCH 7/9] do not test all rubies every time --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) 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 From 02435252e550eb16cf470f6c5377512ce974a53e Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Wed, 14 Mar 2018 17:18:04 +0000 Subject: [PATCH 8/9] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) 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. From f411294042bd1ac796f50e49a40a97a5d49fa589 Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sat, 17 Mar 2018 23:59:49 +0000 Subject: [PATCH 9/9] matching channel against all IP addresses for the hostname; prefering Resolv.getaddress, despite IPv4 preference (there is some inconsistency with IPv6 addresses using TCPSocket.getaddress) --- lib/httpx/channel.rb | 13 +++++++------ lib/httpx/io.rb | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/httpx/channel.rb b/lib/httpx/channel.rb index fc08ab01..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 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