Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add read_nonblock to regular and Chunked encoding readers

  • Loading branch information...
commit 44cc01679f6a5274dcca7576eb39f2ca64375ae0 1 parent f704ae2
@wycats wycats authored
Showing with 198 additions and 50 deletions.
  1. +72 −44 lib/net2/http/readers.rb
  2. +126 −6 test/test_reader.rb
View
116 lib/net2/http/readers.rb
@@ -1,14 +1,60 @@
module Net2
class HTTP
- class BodyReader
- def initialize(socket, endpoint, content_length)
+ class AbstractReader
+ BUFSIZE = 1024
+
+ def initialize(socket, endpoint)
@socket = socket
@endpoint = endpoint
- @content_length = content_length
+ end
+
+ def read(bufsize, timeout=60)
+ while true
+ read_to_endpoint(bufsize)
+
+ break if eof?
+ wait timeout
+ end
+
+ @endpoint
+ end
+
+ def read_nonblock(len)
+ saw_content = read_to_endpoint(len)
+
+ unless saw_content
+ raise EOFError if eof?
+ raise Errno::EWOULDBLOCK
+ end
+
+ @endpoint
+ ensure
+ @endpoint = ""
+ end
+
+ def wait(timeout=nil)
+ if @io.is_a?(OpenSSL::SSL::SSLSocket)
+ return if IO.select nil, [@socket], nil, timeout
+ else
+ return if IO.select [@socket], nil, nil, timeout
+ end
+
+ raise Timeout::Error
+ end
+ end
+
+ class BodyReader < AbstractReader
+ def initialize(socket, endpoint, content_length)
+ super(socket, endpoint)
+ @content_length = content_length
@read = 0
end
+ def read(timeout=60)
+ super @content_length, timeout
+ end
+
def read_to_endpoint(len=@content_length)
remain = @content_length - @read
@@ -20,26 +66,19 @@ def read_to_endpoint(len=@content_length)
@endpoint << output
@read += output.size
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, OpenSSL::SSL::SSLError
+ return false
end
end
- def wait(timeout=nil)
- if @io.is_a?(OpenSSL::SSL::SSLSocket)
- return if IO.select nil, [@io], nil, timeout
- else
- return if IO.select [@io], nil, nil, timeout
- end
-
- raise Timeout::Error
+ def eof?
+ @content_length - @read == 0
end
end
- class ChunkedBodyReader
- BUFSIZE = 1024
-
+ class ChunkedBodyReader < AbstractReader
def initialize(socket, endpoint="")
- @socket = socket
- @endpoint = endpoint
+ super(socket, endpoint)
+
@raw_buffer = ""
@out_buffer = ""
@@ -54,6 +93,8 @@ def read_to_endpoint(len=nil)
send @state
+ return false if len && @out_buffer.empty?
+
if !len
@endpoint << @out_buffer
@out_buffer = ""
@@ -63,16 +104,24 @@ def read_to_endpoint(len=nil)
@endpoint << @out_buffer
@out_buffer = ""
end
+
+ return true
end
def read(timeout=60)
- while true
- @endpoint << read_to_endpoint(1024)
- break if eof?
- wait timeout
- end
+ super BUFSIZE, timeout
+ end
- @endpoint
+ def eof?
+ @eof && @out_buffer.empty? && @raw_buffer.empty?
+ end
+
+ private
+ def fill_buffer
+ @raw_buffer << @socket.read_nonblock(BUFSIZE)
+ return true
+ rescue Errno::EWOULDBLOCK, EOFError
+ return false
end
def process_size
@@ -94,6 +143,7 @@ def process_size
process_chunk
end
+ # TODO: Make this handle chunk metadata
def process_chunk
if @raw_buffer.size > @size
@out_buffer << @raw_buffer.slice!(0, @size)
@@ -111,28 +161,6 @@ def process_trailer
raise EOFError if eof?
@eof = true
end
-
- def eof?
- @eof && @out_buffer.empty? && @raw_buffer.empty?
- end
-
- private
- def fill_buffer
- @raw_buffer << @socket.read_nonblock(BUFSIZE)
- return true
- rescue Errno::EWOULDBLOCK, EOFError
- return false
- end
-
- def wait(timeout=nil)
- if @io.is_a?(OpenSSL::SSL::SSLSocket)
- return if IO.select nil, [@socket], nil, timeout
- else
- return if IO.select [@socket], nil, nil, timeout
- end
-
- raise Timeout::Error
- end
end
end
end
View
132 test/test_reader.rb
@@ -58,6 +58,84 @@ def test_blocking
@reader.read_to_endpoint 10
end
end
+
+ class TestBuffer
+ def initialize(queue)
+ @queue = queue
+ @string = ""
+ end
+
+ def <<(str)
+ @string << str
+ @queue.push :continue
+ end
+
+ def to_str
+ @string
+ end
+ end
+
+ def test_read_entire_body
+ read_queue = Queue.new
+ write_queue = Queue.new
+
+ Thread.new do
+ @write.write @body.slice(0,50)
+
+ read_queue.push :continue
+ write_queue.pop
+
+ @write.write @body[50..-2]
+
+ write_queue.pop
+
+ @write.write @body[-1..-1]
+ end
+
+ read_queue.pop
+
+ buffer = TestBuffer.new(write_queue)
+ @reader = Net2::HTTP::BodyReader.new(@read, buffer, @body.bytesize)
+ out = @reader.read
+
+ assert_equal @body, out.to_str
+ end
+
+ def test_read_nonblock
+ @reader = Net2::HTTP::BodyReader.new(@read, "", @body.bytesize)
+
+ @write.write @body.slice(0,50)
+
+ buf = ""
+ buf << @reader.read_nonblock(20)
+ buf << @reader.read_nonblock(35)
+
+ assert_raises Errno::EWOULDBLOCK do
+ @reader.read_nonblock(10)
+ end
+
+ @write.write @body[50..-2]
+
+ buf << @reader.read_nonblock(1000)
+
+ assert_raises Errno::EWOULDBLOCK do
+ @reader.read_nonblock(10)
+ end
+
+ @write.write @body[-1..-1]
+
+ buf << @reader.read_nonblock(100)
+
+ assert_raises EOFError do
+ @reader.read_nonblock(10)
+ end
+
+ assert_raises EOFError do
+ @reader.read_nonblock(10)
+ end
+
+ assert_equal @body, buf
+ end
end
class TestChunkedBodyReader < Test::Unit::TestCase
@@ -145,15 +223,52 @@ def test_multi_chunks
end
end
+ def test_read_nonblock
+ @write.write 50.to_s(16)
+ @write.write "\r\n"
+ @write.write @body.slice(0,50)
+
+ buf = @reader.read_nonblock(20)
+ buf << @reader.read_nonblock(35)
+
+ assert_raises Errno::EWOULDBLOCK do
+ @reader.read_nonblock 10
+ end
+
+ @write.write "\r\n"
+ rest = @body[50..-1]
+ @write.write rest.size.to_s(16)
+ @write.write "\r\n"
+ @write.write rest
+
+ buf << @reader.read_nonblock(1000)
+
+ assert_raises Errno::EWOULDBLOCK do
+ @reader.read_nonblock 10
+ end
+
+ @write.write "\r\n0\r\n"
+
+ assert_raises EOFError do
+ @reader.read_nonblock(100)
+ end
+
+ assert_equal @body, buf
+
+ assert_raises EOFError do
+ @reader.read_nonblock(100)
+ end
+ end
+
class TestBuffer
def initialize(queue)
- @queue = queue
+ @write_queue = queue
@string = ""
end
def <<(str)
@string << str
- @queue.push :continue
+ @write_queue.push :continue
end
def to_str
@@ -162,14 +277,16 @@ def to_str
end
def test_read_entire_body
- queue = Queue.new
+ write_queue = Queue.new
+ read_queue = Queue.new
Thread.new do
@write.write 50.to_s(16)
@write.write "\r\n"
@write.write @body.slice(0,50)
- queue.pop
+ read_queue.push :continue
+ write_queue.pop
@write.write "\r\n"
rest = @body[50..-1]
@@ -177,12 +294,15 @@ def test_read_entire_body
@write.write "\r\n"
@write.write rest
- queue.pop
+ write_queue.pop
@write.write "\r\n0\r\n"
+
end
- buffer = TestBuffer.new(queue)
+ read_queue.pop
+
+ buffer = TestBuffer.new(write_queue)
@reader = Net2::HTTP::ChunkedBodyReader.new(@read, buffer)
out = @reader.read
Please sign in to comment.
Something went wrong with that request. Please try again.