lib/http/protocol/http2/stream.rb in http-protocol-0.14.0 vs lib/http/protocol/http2/stream.rb in http-protocol-0.15.0

- old
+ new

@@ -113,11 +113,11 @@ def closed? @state == :closed or @state == :reset end def send_headers? - @state == :idle or @state == :reseved_local or @state == :open or @state == :half_closed_remote + @state == :idle or @state == :reserved_local or @state == :open or @state == :half_closed_remote end def send_failure(status, reason) if send_headers? send_headers(nil, [ @@ -125,10 +125,12 @@ ['reason', reason] ], END_STREAM) else send_reset_stream(PROTOCOL_ERROR) end + + return nil end private def write_headers(priority, headers, flags = 0) data = @connection.encode_headers(headers) @@ -148,11 +150,11 @@ if frame.end_stream? @state = :half_closed_local else @state = :open end - elsif @state == :reseved_local + elsif @state == :reserved_local frame = write_headers(*args) @state = :half_closed_remote elsif @state == :open frame = write_headers(*args) @@ -244,11 +246,11 @@ else @state = :open end @headers = process_headers(frame) - elsif @state == :reseved_remote + elsif @state == :reserved_remote @state = :half_closed_local @headers = process_headers(frame) elsif @state == :open if frame.end_stream? @@ -304,9 +306,68 @@ return frame.unpack else raise ProtocolError, "Cannot reset stream in state: #{@state}" end + end + + # A normal request is client request -> server response -> client. + # A push promise is server request -> client -> server response -> client. + # The server generates the same set of headers as if the client was sending a request, and sends these to the client. The client can reject the request by resetting the (new) stream. Otherwise, the server will start sending a response as if the client had send the request. + private def write_push_promise(stream_id, headers, flags = 0, **options) + data = @connection.encode_headers(headers) + + frame = PushPromiseFrame.new(@id, flags) + frame.pack(stream_id, data, maximum_size: @connection.maximum_frame_size) + + write_frame(frame) + + return frame + end + + def reserved_local! + if @state == :idle + @state = :reserved_local + else + raise ProtocolError, "Cannot reserve stream in state: #{@state}" + end + end + + def reserved_remote! + if @state == :idle + @state = :reserved_remote + else + raise ProtocolError, "Cannot reserve stream in state: #{@state}" + end + end + + def create_promise_stream(headers, stream_id) + @connection.create_stream(stream_id) + end + + # Server push is semantically equivalent to a server responding to a request; however, in this case, that request is also sent by the server, as a PUSH_PROMISE frame. + # @param headers [Hash] contains a complete set of request header fields that the server attributes to the request. + def send_push_promise(headers, stream_id = @connection.next_stream_id) + if @state == :open or @state == :half_closed_remote + promised_stream = self.create_promise_stream(headers, stream_id) + promised_stream.reserved_local! + + write_push_promise(promised_stream.id, headers) + + return promised_stream + else + raise ProtocolError, "Cannot send push promise in state: #{@state}" + end + end + + def receive_push_promise(frame) + promised_stream_id, data = frame.unpack + headers = @connection.decode_headers(data) + + stream = self.create_promise_stream(headers, promised_stream_id) + stream.reserved_remote! + + return stream, headers end end end end end