-
-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure wire always contains a full H/2 frame #15
Conversation
Verified the fix works with the following script (modification to the repro in #14) # requires
require "stringio"
require "async/reactor"
class FunkyIO
def initialize
@f = Protocol::HTTP2::DataFrame.new(401, 0, Protocol::HTTP2::DataFrame::TYPE, 13, "a" * 13)
@sio = StringIO.new
@f.write_header(@sio)
@sio.rewind
@state = :read_header
end
def peek(size, buf = nil)
if @state == :read_header || @state == :try_header_again
res = @sio.read(size, buf)
case @state
when :read_header
@state = :now_timeout
when :try_header_again
@state = :read_frame
end
@sio = StringIO.new
@f.write_header(@sio)
@f.write_payload(@sio)
@sio.rewind
return res
end
end
def read(size, buf = nil)
case @state
when :now_timeout
@state = :try_header_again
raise Async::TimeoutError
when :read_frame
res = @sio.read(size, buf)
res
end
end
end
f = Protocol::HTTP2::Framer.new(FunkyIO.new)
Async::Reactor.run do
begin
p(f.read_frame)
rescue Async::TimeoutError
# try again
end
p(f.read_frame)
end outputs |
I released |
Hi @ioquatix really sorry for disappearing on this PR for a bit, got caught up with other stuff! I've updated it a bit, and tested it with the follwing script: # requires
require "stringio"
require "async"
require "async/reactor"
require "protocol/http2/framer"
require "protocol/http2/data_frame"
class FunkyIO
def initialize
@f = Protocol::HTTP2::DataFrame.new(401, 0, Protocol::HTTP2::DataFrame::TYPE, 13, "a" * 13)
@sio = StringIO.new
@f.write_header(@sio)
@sio.rewind
@state = :read_header
end
def sync=(x)
end
def read_nonblock(size, buf = nil, exception)
puts "read_nonblock(#{size}) state: #{@state}"
if @state == :read_header || @state == :try_header_again
res = @sio.read(size, buf)
case @state
when :read_header
@state = :now_timeout
when :try_header_again
@state = :read_frame
end
@sio = StringIO.new
@f.write_payload(@sio)
@sio.rewind
return res
elsif @state == :now_timeout
@state = :try_header_again
raise Async::TimeoutError
elsif @state == :read_frame
return @sio.read(size, buf)
end
end
end
f = Protocol::HTTP2::Framer.new(FunkyIO.new)
Async::Reactor.run do
begin
p(f.read_frame)
rescue Async::TimeoutError
# try again
end
p(f.read_frame)
end which outputs
I'm not sure why but |
One note: this PR now implicitly adds a requirement that the I believe |
@ioquatix do you know why the test suite might be timing out here? |
The next step for this code is to work with standard IO instances. However, I don't think a method like |
After considering this further, I believe we should adopt the |
I'm going to merge this into another branch so I can finish it off. |
Follow-up to socketry/async-io#72 to fix #14
Updates
read_header
to useStream#peek
instead of `Stream#read. We maintain the invariant that the read buffer / wire is always frame-aligned. Instead of reading data off the wire when reading the header for an H/2 frame, we just peek the header and then read the full frame.Types of Changes
Contribution