Browse Source

Handle last remote stream ID in cow_http2_machine

New function set_last_streamid/1 sets the last accepted stream ID
to the last known remote stream ID. Frames with a remote stream ID
greater than this are thereafter discarded by frame/2, which returns
{ok, Http2Machine} for such frames.
Viktor Söderqvist 4 years ago
parent
commit
6deddc7d33
1 changed files with 32 additions and 3 deletions
  1. 32 3
      src/cow_http2_machine.erl

+ 32 - 3
src/cow_http2_machine.erl

@@ -33,6 +33,7 @@
 -export([get_local_setting/2]).
 -export([get_remote_settings/1]).
 -export([get_last_streamid/1]).
+-export([set_last_streamid/1]).
 -export([get_stream_local_buffer_size/2]).
 -export([get_stream_local_state/2]).
 -export([get_stream_remote_state/2]).
@@ -145,6 +146,7 @@
 	%% Stream identifiers.
 	local_streamid :: pos_integer(), %% The next streamid to be used.
 	remote_streamid = 0 :: non_neg_integer(), %% The last streamid received.
+	last_remote_streamid = 16#7fffffff :: non_neg_integer(), %% Used in GOAWAY.
 
 	%% Currently active HTTP/2 streams. Streams may be initiated either
 	%% by the client or by the server through PUSH_PROMISE frames.
@@ -309,11 +311,11 @@ frame(Frame, State=#http2_machine{state=settings, preface_timer=TRef}) ->
 	end,
 	settings_frame(Frame, State#http2_machine{state=normal, preface_timer=undefined});
 frame(Frame, State=#http2_machine{state={continuation, _, _}}) ->
-	continuation_frame(Frame, State);
+	maybe_discard_result(continuation_frame(Frame, State));
 frame(settings_ack, State=#http2_machine{state=normal}) ->
 	settings_ack_frame(State);
 frame(Frame, State=#http2_machine{state=normal}) ->
-	case element(1, Frame) of
+	Result = case element(1, Frame) of
 		data -> data_frame(Frame, State);
 		headers -> headers_frame(Frame, State);
 		priority -> priority_frame(Frame, State);
@@ -326,7 +328,26 @@ frame(Frame, State=#http2_machine{state=normal}) ->
 		window_update -> window_update_frame(Frame, State);
 		continuation -> unexpected_continuation_frame(Frame, State);
 		_ -> ignored_frame(State)
-	end.
+	end,
+	maybe_discard_result(Result).
+
+%% RFC7540 6.9. After sending a GOAWAY frame, the sender can discard frames for
+%% streams initiated by the receiver with identifiers higher than the identified
+%% last stream. However, any frames that alter connection state cannot be
+%% completely ignored. For instance, HEADERS, PUSH_PROMISE, and CONTINUATION
+%% frames MUST be minimally processed to ensure the state maintained for header
+%% compression is consistent.
+maybe_discard_result(FrameResult={ok, Result, State=#http2_machine{mode=Mode,
+		last_remote_streamid=MaxID}})
+		when element(1, Result) =/= goaway ->
+	case element(2, Result) of
+		StreamID when StreamID > MaxID, not ?IS_LOCAL(Mode, StreamID) ->
+			{ok, State};
+		_StreamID ->
+			FrameResult
+	end;
+maybe_discard_result(FrameResult) ->
+	FrameResult.
 
 %% DATA frame.
 
@@ -1529,6 +1550,14 @@ default_setting_value(enable_connect_protocol) -> false.
 get_last_streamid(#http2_machine{remote_streamid=RemoteStreamID}) ->
 	RemoteStreamID.
 
+%% Set last accepted streamid to the last known streamid, for the purpose
+%% ignoring frames for remote streams created after sending GOAWAY.
+
+-spec set_last_streamid(http2_machine()) -> {cow_http2:streamid(), http2_machine()}.
+set_last_streamid(State=#http2_machine{remote_streamid=StreamID,
+		last_remote_streamid=LastStreamID}) when StreamID =< LastStreamID->
+	{StreamID, State#http2_machine{last_remote_streamid = StreamID}}.
+
 %% Retrieve the local buffer size for a stream.
 
 -spec get_stream_local_buffer_size(cow_http2:streamid(), http2_machine())