From a5f4d11cdb9465b1ffa2892b3d84ee53b8962930 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 26 Feb 2010 03:05:21 -0800 Subject: tee_input: avoid instance variables, it's a struct We'll use struct members exclusively from now on instead of throwing ivars into the mix. This allows us to _unofficially_ support direct access to more members easily. Unofficial extensions may include the ability to splice(2)/tee(2) for better performance. This also makes our object size smaller across all Ruby implementations as well, too (helps Rainbows! out). --- lib/unicorn/tee_input.rb | 78 ++++++++++++++++++++++----------------------- test/unit/test_tee_input.rb | 30 ++++++++--------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/unicorn/tee_input.rb b/lib/unicorn/tee_input.rb index bb86c40..a7cb4b9 100644 --- a/lib/unicorn/tee_input.rb +++ b/lib/unicorn/tee_input.rb @@ -13,19 +13,19 @@ module Unicorn # # When processing uploads, Unicorn exposes a TeeInput object under # "rack.input" of the Rack environment. - class TeeInput < Struct.new(:socket, :req, :parser, :buf) + class TeeInput < Struct.new(:socket, :req, :parser, :buf, :len, :tmp, :buf2) # Initializes a new TeeInput object. You normally do not have to call # this unless you are writing an HTTP server. def initialize(*args) super(*args) - @size = parser.content_length - @tmp = @size && @size < Const::MAX_BODY ? StringIO.new("") : Util.tmpio - @buf2 = buf.dup + self.len = parser.content_length + self.tmp = len && len < Const::MAX_BODY ? StringIO.new("") : Util.tmpio + self.buf2 = buf.dup if buf.size > 0 - parser.filter_body(@buf2, buf) and finalize_input - @tmp.write(@buf2) - @tmp.seek(0) + parser.filter_body(buf2, buf) and finalize_input + tmp.write(buf2) + tmp.seek(0) end end @@ -40,16 +40,16 @@ module Unicorn # all of the input stream before returning since there's no other # way to determine the size of the request body beforehand. def size - @size and return @size + len and return len if socket - pos = @tmp.pos - while tee(Const::CHUNK_SIZE, @buf2) + pos = tmp.pos + while tee(Const::CHUNK_SIZE, buf2) end - @tmp.seek(pos) + tmp.seek(pos) end - @size = @tmp.size + self.len = tmp.size end # :call-seq: @@ -72,22 +72,22 @@ module Unicorn # any data and only block when nothing is available (providing # IO#readpartial semantics). def read(*args) - socket or return @tmp.read(*args) + socket or return tmp.read(*args) length = args.shift if nil == length - rv = @tmp.read || "" - while tee(Const::CHUNK_SIZE, @buf2) - rv << @buf2 + rv = tmp.read || "" + while tee(Const::CHUNK_SIZE, buf2) + rv << buf2 end rv else - rv = args.shift || @buf2.dup - diff = @tmp.size - @tmp.pos + rv = args.shift || buf2.dup + diff = tmp.size - tmp.pos if 0 == diff ensure_length(tee(length, rv), length) else - ensure_length(@tmp.read(diff > length ? length : diff, rv), length) + ensure_length(tmp.read(diff > length ? length : diff, rv), length) end end end @@ -102,26 +102,26 @@ module Unicorn # This takes zero arguments for strict Rack::Lint compatibility, # unlike IO#gets. def gets - socket or return @tmp.gets + socket or return tmp.gets nil == $/ and return read - orig_size = @tmp.size - if @tmp.pos == orig_size - tee(Const::CHUNK_SIZE, @buf2) or return nil - @tmp.seek(orig_size) + orig_size = tmp.size + if tmp.pos == orig_size + tee(Const::CHUNK_SIZE, buf2) or return nil + tmp.seek(orig_size) end - line = @tmp.gets # cannot be nil here since size > pos + line = tmp.gets # cannot be nil here since size > pos $/ == line[-$/.size, $/.size] and return line - # unlikely, if we got here, then @tmp is at EOF + # unlikely, if we got here, then tmp is at EOF begin - orig_size = @tmp.pos - tee(Const::CHUNK_SIZE, @buf2) or break - @tmp.seek(orig_size) - line << @tmp.gets + orig_size = tmp.pos + tee(Const::CHUNK_SIZE, buf2) or break + tmp.seek(orig_size) + line << tmp.gets $/ == line[-$/.size, $/.size] and return line - # @tmp is at EOF again here, retry the loop + # tmp is at EOF again here, retry the loop end while true line @@ -147,7 +147,7 @@ module Unicorn # the offset (zero) of the +ios+ pointer. Subsequent reads will # start from the beginning of the previously-buffered input. def rewind - @tmp.rewind # Rack does not specify what the return value is here + tmp.rewind # Rack does not specify what the return value is here end private @@ -160,7 +160,7 @@ module Unicorn # _entire_ request has been sent, and those will not have # raised EOFError on us. socket.close if socket - raise ClientShutdown, "bytes_read=#{@tmp.size}", [] + raise ClientShutdown, "bytes_read=#{tmp.size}", [] when HttpParserError e.set_backtrace([]) end @@ -173,8 +173,8 @@ module Unicorn def tee(length, dst) unless parser.body_eof? if parser.filter_body(dst, socket.readpartial(length, buf)).nil? - @tmp.write(dst) - @tmp.seek(0, IO::SEEK_END) # workaround FreeBSD/OSX + MRI 1.8.x bug + tmp.write(dst) + tmp.seek(0, IO::SEEK_END) # workaround FreeBSD/OSX + MRI 1.8.x bug return dst end end @@ -201,13 +201,13 @@ module Unicorn # streaming input bodies, this is a no-op for # "Transfer-Encoding: chunked" requests. def ensure_length(dst, length) - # @size is nil for chunked bodies, so we can't ensure length for those + # len is nil for chunked bodies, so we can't ensure length for those # since they could be streaming bidirectionally and we don't want to # block the caller in that case. - return dst if dst.nil? || @size.nil? + return dst if dst.nil? || len.nil? - while dst.size < length && tee(length - dst.size, @buf2) - dst << @buf2 + while dst.size < length && tee(length - dst.size, buf2) + dst << buf2 end dst diff --git a/test/unit/test_tee_input.rb b/test/unit/test_tee_input.rb index 403f698..ee81a87 100644 --- a/test/unit/test_tee_input.rb +++ b/test/unit/test_tee_input.rb @@ -71,8 +71,8 @@ class TestTeeInput < Test::Unit::TestCase ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) assert_equal 0, @parser.content_length assert @parser.body_eof? - assert_equal StringIO, ti.instance_eval { @tmp.class } - assert_equal 0, ti.instance_eval { @tmp.pos } + assert_equal StringIO, ti.tmp.class + assert_equal 0, ti.tmp.pos assert_equal 5, ti.size assert_equal 'hello', ti.read assert_equal '', ti.read @@ -99,8 +99,8 @@ class TestTeeInput < Test::Unit::TestCase ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) assert_equal 0, @parser.content_length assert @parser.body_eof? - assert_kind_of File, ti.instance_eval { @tmp } - assert_equal 0, ti.instance_eval { @tmp.pos } + assert_kind_of File, ti.tmp + assert_equal 0, ti.tmp.pos assert_equal Unicorn::Const::MAX_BODY + 1, ti.size end @@ -125,9 +125,9 @@ class TestTeeInput < Test::Unit::TestCase ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) assert_equal Unicorn::Const::MAX_BODY, @parser.content_length assert ! @parser.body_eof? - assert_kind_of File, ti.instance_eval { @tmp } - assert_equal 0, ti.instance_eval { @tmp.pos } - assert_equal 1, ti.instance_eval { @tmp.size } + assert_kind_of File, ti.tmp + assert_equal 0, ti.tmp.pos + assert_equal 1, ti.tmp.size assert_equal Unicorn::Const::MAX_BODY + 1, ti.size nr = Unicorn::Const::MAX_BODY / 4 pid = fork { @@ -165,19 +165,19 @@ class TestTeeInput < Test::Unit::TestCase @wr.close ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) assert_nil @parser.content_length - assert_nil ti.instance_eval { @size } + assert_nil ti.len assert ! @parser.body_eof? assert_equal 25, ti.size assert @parser.body_eof? - assert_equal 25, ti.instance_eval { @size } - assert_equal 0, ti.instance_eval { @tmp.pos } + assert_equal 25, ti.len + assert_equal 0, ti.tmp.pos assert_nothing_raised { ti.rewind } - assert_equal 0, ti.instance_eval { @tmp.pos } + assert_equal 0, ti.tmp.pos assert_equal 'abcdeabcdeabcdeabcde', ti.read(20) - assert_equal 20, ti.instance_eval { @tmp.pos } + assert_equal 20, ti.tmp.pos assert_nothing_raised { ti.rewind } - assert_equal 0, ti.instance_eval { @tmp.pos } - assert_kind_of File, ti.instance_eval { @tmp } + assert_equal 0, ti.tmp.pos + assert_kind_of File, ti.tmp status = nil assert_nothing_raised { pid, status = Process.waitpid2(pid) } assert status.success? @@ -203,7 +203,7 @@ class TestTeeInput < Test::Unit::TestCase } ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) assert_nil @parser.content_length - assert_nil ti.instance_eval { @size } + assert_nil ti.len assert ! @parser.body_eof? chunks.each do |chunk| wr.write('.') -- cgit v1.2.3-24-ge0c7