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