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