http_spew RubyGem user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: http_spew-public@bogomips.org
Subject: [PATCH] remove most uses of kgio
Date: Thu,  9 Mar 2017 20:43:24 +0000	[thread overview]
Message-ID: <20170309204324.1718-1-e@80x24.org> (raw)

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<kcar>, [ "~> 0.3", ">= 0.3.1"])
   s.add_dependency(%q<kgio>, "~> 2.6")
   s.add_development_dependency(%q<olddoc>, "~> 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


             reply	other threads:[~2017-03-09 20:43 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-03-09 20:43 Eric Wong [this message]
2017-03-10  0:44 ` [PATCH 2/1] make kgio entirely optional Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://yhbt.net/http_spew/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170309204324.1718-1-e@80x24.org \
    --to=e@80x24.org \
    --cc=http_spew-public@bogomips.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhbt.net/http_spew.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox