From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.0 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id E2931202F8 for ; Thu, 9 Mar 2017 20:43:24 +0000 (UTC) From: Eric Wong To: http_spew-public@bogomips.org Subject: [PATCH] remove most uses of kgio Date: Thu, 9 Mar 2017 20:43:24 +0000 Message-Id: <20170309204324.1718-1-e@80x24.org> List-Id: And use Ruby 2.3+ *_nonblock(... exception: false) instead. Since most of kgio-specific features are in newer releases of Ruby, we can start phasing kgio out to avoid hassling users with installing more software. We can still benefit from Kgio.poll over IO.select at the moment, but we can make it optional in the future. --- http_spew.gemspec | 2 +- lib/http_spew.rb | 1 + lib/http_spew/chunky_pipe.rb | 17 +++++++++--- lib/http_spew/class_methods.rb | 4 +-- lib/http_spew/request.rb | 56 ++++++++++++++++++++++++++-------------- test/test_hit_n_run.rb | 8 +++--- test/test_request.rb | 10 +++---- test/test_unexpected_response.rb | 2 +- 8 files changed, 64 insertions(+), 36 deletions(-) diff --git a/http_spew.gemspec b/http_spew.gemspec index 4bede9b..d4d38da 100644 --- a/http_spew.gemspec +++ b/http_spew.gemspec @@ -18,6 +18,6 @@ Gem::Specification.new do |s| s.add_dependency(%q, [ "~> 0.3", ">= 0.3.1"]) s.add_dependency(%q, "~> 2.6") s.add_development_dependency(%q, "~> 1.0") - s.required_ruby_version = '>= 2.1' + s.required_ruby_version = '>= 2.3' s.licenses = %w(GPL-2.0+) end diff --git a/lib/http_spew.rb b/lib/http_spew.rb index e6bde3b..b658627 100644 --- a/lib/http_spew.rb +++ b/lib/http_spew.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +require "io/wait" require "kgio" require "kcar" diff --git a/lib/http_spew/chunky_pipe.rb b/lib/http_spew/chunky_pipe.rb index 4c51663..590d2f1 100644 --- a/lib/http_spew/chunky_pipe.rb +++ b/lib/http_spew/chunky_pipe.rb @@ -3,17 +3,28 @@ # This is a OS-level pipe that overrides IO#read to provide # IO#readpartial-like semantics while remaining Rack::Lint-compatible # for EOF, meaning we return nil on EOF instead of raising EOFError. -class HTTP_Spew::ChunkyPipe < Kgio::Pipe +class HTTP_Spew::ChunkyPipe < IO # other threads may force an error to be raised in the +read+ # method attr_accessor :error + class << self + alias new pipe + end + # Override IO#read to behave like IO#readpartial, but still return +nil+ # on EOF instead of raising EOFError. - def read(*args) + def read(len = 16384, buf = '') check_err! - kgio_read(*args) || check_err! || close + case read_nonblock(len, buf, exception: false) + when nil + return check_err! || close + when :wait_readable + wait_readable # retry + else + return buf + end while true end def check_err! diff --git a/lib/http_spew/class_methods.rb b/lib/http_spew/class_methods.rb index f068f6b..79b3e47 100644 --- a/lib/http_spew/class_methods.rb +++ b/lib/http_spew/class_methods.rb @@ -53,7 +53,7 @@ module HTTP_Spew::ClassMethods # If +need+ is fullfilled, it closes all incomplete requests. def wait_mt(need, requests, timeout) ready, failed = [], [] - r, w = Kgio::Pipe.new + r, w = IO.pipe active = [] t = [ timeout ] requests.each_with_index do |req, i| @@ -68,7 +68,7 @@ module HTTP_Spew::ClassMethods end end begin - with_timeout(t) { r.kgio_wait_readable(t[0]) } + with_timeout(t) { r.wait_readable(t[0]) } req_idx = r.read(2).unpack("v".freeze)[0] thr = active[req_idx] with_timeout(t) { thr.join(t[0]) } diff --git a/lib/http_spew/request.rb b/lib/http_spew/request.rb index 1d02a98..1647d23 100644 --- a/lib/http_spew/request.rb +++ b/lib/http_spew/request.rb @@ -1,4 +1,6 @@ # -*- encoding: binary -*- +require 'socket' + # This is the base class actually capable of making a normal HTTP request class HTTP_Spew::Request @@ -21,10 +23,9 @@ class HTTP_Spew::Request # # +sock+ may be the String representing an address created with # +Socket.pack_sockaddr_un+ or +Socket.pack_sockaddr_in+, or it - # may be an actual IO object with Kgio::SocketMethods mixed in - # (e.g. Kgio::Socket) + # may be an actual Socket object def initialize(env, input, sock, allow = nil) - @to_io = Kgio::SocketMethods === sock ? sock : Kgio::Socket.start(sock) + @to_io = BasicSocket === sock ? sock : start_sock(sock) if Hash === env @buf, @input = env_to_headers(env, input) else @@ -37,18 +38,21 @@ class HTTP_Spew::Request # returns :wait_readable or :wait_writable if busy def resume if @buf - case rv = @to_io.kgio_trywrite(@buf) - when String # unlikely - @buf = rv # loop retry, socket buffer could've expanded - when Symbol - return rv - else # done writing, read more - @buf = @input ? @input.read(0x4000, @buf) : nil + case w = @to_io.write_nonblock(@buf, exception: false) + when :wait_writable, :wait_readable + return w + else # Integer + len = @buf.size + if w == len + @buf = @input ? @input.read(0x4000, @buf) : nil + else + tmp = @buf.byteslice(w, len - w) + @buf.clear + @buf = tmp # loop retry, socket buffer could've expanded + end end while @buf - read_response - else - read_response end + read_response end # returns a 3-element Rack response array on successful completion @@ -64,7 +68,7 @@ class HTTP_Spew::Request timeout -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) while :wait_readable == (rv = read_response) && timeout >= 0.0 t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @to_io.kgio_wait_readable(timeout) if timeout > 0.0 + @to_io.wait_readable(timeout) if timeout > 0.0 timeout -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) end rv @@ -78,7 +82,7 @@ class HTTP_Spew::Request # Users do not need to call this directly, +resume+ will return the result # of this. def read_response - buf = @to_io.kgio_trypeek(0x4000) or + buf = @to_io.recv_nonblock(0x4000, Socket::MSG_PEEK, exception: false) or raise HttpSpew::EOF, "upstream server closed connection", [] String === buf or return buf @@ -91,7 +95,7 @@ class HTTP_Spew::Request end # discard the header data from the socket buffer - (hdr_len -= buf.size) > 0 and @to_io.kgio_read(hdr_len, buf) + (hdr_len -= buf.size) > 0 and @to_io.read(hdr_len, buf) @response = r << self end @@ -102,10 +106,15 @@ class HTTP_Spew::Request # Called by Rack servers to write the response to a client def each buf = "" - while @to_io.kgio_read(0x4000, buf) + case @to_io.read_nonblock(0x4000, buf, exception: false) + when :wait_readable + @to_io.wait_readable + when nil + buf.clear + return + else yield buf - end - buf.clear + end while true end # Used internally by various HTTP_Spew elements to report errors @@ -121,4 +130,13 @@ class HTTP_Spew::Request @to_io.close @input = nil end + + def start_sock(ai) + ai = Addrinfo.new(ai) unless Addrinfo === ai + sock = Socket.new(ai.afamily, :SOCK_STREAM) + case sock.connect_nonblock(ai, exception: false) + when 0, :wait_writable + end + sock + end end diff --git a/test/test_hit_n_run.rb b/test/test_hit_n_run.rb index 751a8bf..425f2d5 100644 --- a/test/test_hit_n_run.rb +++ b/test/test_hit_n_run.rb @@ -19,7 +19,8 @@ class TestHitNRun < Test::Unit::TestCase end def test_request_with_existing_socket - sock = Kgio::Socket.new(@sockaddr) + sock = TCPSocket.new(@addr, @port) + assert(BasicSocket === sock) req = HTTP_Spew::HitNRun.new(@env, nil, sock) assert_equal sock, req.to_io assert_nothing_raised { req.close } @@ -30,8 +31,7 @@ class TestHitNRun < Test::Unit::TestCase req = HTTP_Spew::HitNRun.new(@env, nil, @sockaddr) sym = req.resume if sym == :wait_writable - set = Kgio.poll({req => sym}, 100) - assert_equal [ req ], set.keys + assert req.to_io.wait_writable(0.1) sym = req.resume end assert_equal HTTP_Spew::HitNRun::RESPONSE.object_id, sym.object_id @@ -40,7 +40,7 @@ class TestHitNRun < Test::Unit::TestCase def test_request_loop req = HTTP_Spew::HitNRun.new(@env, nil, @sockaddr) until Array === (rv = req.resume) - Kgio.poll(req => rv) + req.__send__(rv) end assert_equal HTTP_Spew::HitNRun::RESPONSE.object_id, rv.object_id end diff --git a/test/test_request.rb b/test/test_request.rb index 67ad70c..b8165c4 100644 --- a/test/test_request.rb +++ b/test/test_request.rb @@ -19,7 +19,7 @@ class TestRequest < Test::Unit::TestCase end def test_request_with_existing_socket - sock = Kgio::Socket.new(@sockaddr) + sock = TCPSocket.new(@addr, @port) req = HTTP_Spew::Request.new(@env, nil, sock) assert_equal sock, req.to_io assert_nothing_raised { req.close } @@ -31,13 +31,11 @@ class TestRequest < Test::Unit::TestCase sym = req.resume assert_kind_of(Symbol, sym) if sym == :wait_writable - set = Kgio.poll({req => sym}, 1000) - assert_equal [ req ], set.keys + assert req.to_io.wait_writable(1) sym = req.resume end assert_equal :wait_readable, sym - set = Kgio.poll({req => sym}, 1000) - assert_equal [ req ], set.keys + assert req.to_io.wait_readable(1) rv = req.resume assert_equal req, rv[2] end @@ -45,7 +43,7 @@ class TestRequest < Test::Unit::TestCase def test_request_loop req = HTTP_Spew::Request.new(@env, nil, @sockaddr) until Array === (rv = req.resume) - Kgio.poll(req => rv) + req.to_io.__send__(rv) # wait_readable/wait_writable end assert_kind_of Array, rv assert_equal 3, rv.size diff --git a/test/test_unexpected_response.rb b/test/test_unexpected_response.rb index bbd63be..2caff05 100644 --- a/test/test_unexpected_response.rb +++ b/test/test_unexpected_response.rb @@ -19,7 +19,7 @@ class TestRequest < Test::Unit::TestCase end def test_request_with_existing_socket - sock = Kgio::Socket.new(@sockaddr) + sock = TCPSocket.new(@addr, @port) req = HTTP_Spew::Request.new(@env, nil, sock) assert_equal sock, req.to_io assert_nothing_raised { req.close } -- EW