class Async::HTTP::Protocol::HTTP2::Stream
Attributes
headers[RW]
input[R]
Public Class Methods
new(*)
click to toggle source
Calls superclass method
# File lib/async/http/protocol/http2/stream.rb, line 33 def initialize(*) super @headers = nil @trailer = nil # Input buffer, reading request body, or response body (receive_data): @length = nil @input = nil # Output buffer, writing request body or response body (window_updated): @output = nil end
Public Instance Methods
add_header(key, value)
click to toggle source
# File lib/async/http/protocol/http2/stream.rb, line 51 def add_header(key, value) if key == CONNECTION raise ::Protocol::HTTP2::HeaderError, "Connection header is not allowed!" elsif key.start_with? ':' raise ::Protocol::HTTP2::HeaderError, "Invalid pseudo-header #{key}!" elsif key =~ /[A-Z]/ raise ::Protocol::HTTP2::HeaderError, "Invalid upper-case characters in header #{key}!" else @headers.add(key, value) end end
closed(error)
click to toggle source
When the stream transitions to the closed state, this method is called. There are roughly two ways this can happen:
-
A frame is received which causes this stream to enter the closed state. This method will be invoked from the background reader task.
-
A frame is sent which causes this stream to enter the closed state. This method will be invoked from that task.
While the input stream is relatively straight forward, the output stream can trigger the second case above
Calls superclass method
# File lib/async/http/protocol/http2/stream.rb, line 173 def closed(error) super if @input @input.close(error) @input = nil end if @output @output.stop(error) @output = nil end return self end
finish_output(error = nil)
click to toggle source
Called when the output terminates normally.
# File lib/async/http/protocol/http2/stream.rb, line 146 def finish_output(error = nil) trailer = @output&.trailer @output = nil if error send_reset_stream(::Protocol::HTTP2::Error::INTERNAL_ERROR) else # Write trailer? if trailer send_headers(nil, trailer, ::Protocol::HTTP2::END_STREAM) else send_data(nil, ::Protocol::HTTP2::END_STREAM) end end end
prepare_input(length)
click to toggle source
Prepare the input stream which will be used for incoming data frames. @return [Input] the input body.
# File lib/async/http/protocol/http2/stream.rb, line 102 def prepare_input(length) if @input.nil? @input = Input.new(self, length) else raise ArgumentError, "Input body already prepared!" end end
process_data(frame)
click to toggle source
# File lib/async/http/protocol/http2/stream.rb, line 117 def process_data(frame) data = frame.unpack if @input unless data.empty? @input.write(data) end if frame.end_stream? @input.close @input = nil end end return data rescue ::Protocol::HTTP2::ProtocolError raise rescue # Anything else... send_reset_stream(::Protocol::HTTP2::Error::INTERNAL_ERROR) end
process_headers(frame)
click to toggle source
Calls superclass method
# File lib/async/http/protocol/http2/stream.rb, line 73 def process_headers(frame) if @headers.nil? @headers = ::Protocol::HTTP::Headers.new self.receive_initial_headers(super, frame.end_stream?) @trailer = @headers[TRAILER] elsif @trailer and frame.end_stream? self.receive_trailing_headers(super, frame.end_stream?) else raise ::Protocol::HTTP2::HeaderError, "Unable to process headers!" end # TODO this might need to be in an ensure block: if @input and frame.end_stream? @input.close($!) @input = nil end rescue ::Protocol::HTTP2::HeaderError => error Console.logger.error(self, error) send_reset_stream(error.code) end
receive_trailing_headers(headers, end_stream)
click to toggle source
# File lib/async/http/protocol/http2/stream.rb, line 63 def receive_trailing_headers(headers, end_stream) headers.each do |key, value| if @trailer.include?(key) add_header(key, value) else raise ::Protocol::HTTP2::HeaderError, "Cannot add trailer #{key} as it was not specified as a trailer!" end end end
send_body(body, trailer = nil)
click to toggle source
Set the body and begin sending it.
# File lib/async/http/protocol/http2/stream.rb, line 139 def send_body(body, trailer = nil) @output = Output.new(self, body, trailer) @output.start end
update_local_window(frame)
click to toggle source
# File lib/async/http/protocol/http2/stream.rb, line 110 def update_local_window(frame) consume_local_window(frame) # This is done on demand in `Input#read`: # request_window_update end
wait_for_input()
click to toggle source
# File lib/async/http/protocol/http2/stream.rb, line 96 def wait_for_input return @input end
window_updated(size)
click to toggle source
Calls superclass method
# File lib/async/http/protocol/http2/stream.rb, line 163 def window_updated(size) super @output&.window_updated(size) end