aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-04-25 21:32:58 +0200
committerLoïc Hoguin <[email protected]>2018-04-25 21:32:58 +0200
commit7373822b8645f34c03a7bf522ea170c62817d8e8 (patch)
tree386c2805a19eb0f8561e94614a668ff3c736fb75 /src
parent8bd55941aa362632a6e9d8bdca6789cc21ba7e8a (diff)
downloadcowboy-7373822b8645f34c03a7bf522ea170c62817d8e8.tar.gz
cowboy-7373822b8645f34c03a7bf522ea170c62817d8e8.tar.bz2
cowboy-7373822b8645f34c03a7bf522ea170c62817d8e8.zip
Add the max_concurrent_streams h2 option
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_http2.erl23
1 files changed, 20 insertions, 3 deletions
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index c8522ce..3bcd402 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -27,6 +27,7 @@
enable_connect_protocol => boolean(),
env => cowboy_middleware:env(),
inactivity_timeout => timeout(),
+ max_concurrent_streams => non_neg_integer() | infinity,
max_decode_table_size => non_neg_integer(),
max_encode_table_size => non_neg_integer(),
middlewares => [module()],
@@ -206,11 +207,12 @@ init(Parent, Ref, Socket, Transport, Opts, Peer, Sock, Cert, Buffer, _Settings,
settings_init(State, Opts) ->
S0 = setting_from_opt(#{}, Opts, max_decode_table_size,
header_table_size, 4096),
- %% @todo max_concurrent_streams + enforce it
+ S1 = setting_from_opt(S0, Opts, max_concurrent_streams,
+ max_concurrent_streams, infinity),
%% @todo initial_window_size
%% @todo max_frame_size
%% @todo max_header_list_size
- Settings = setting_from_opt(S0, Opts, enable_connect_protocol,
+ Settings = setting_from_opt(S1, Opts, enable_connect_protocol,
enable_connect_protocol, false),
State#state{next_settings=Settings}.
@@ -868,13 +870,24 @@ terminate_all_streams([#stream{id=StreamID, state=StreamState}|Tail], Reason) ->
stream_decode_init(State=#state{decode_state=DecodeState0}, StreamID, IsFin, HeaderBlock) ->
try cow_hpack:decode(HeaderBlock, DecodeState0) of
{Headers, DecodeState} ->
- stream_pseudo_headers_init(State#state{decode_state=DecodeState},
+ stream_enforce_concurrency_limit(State#state{decode_state=DecodeState},
StreamID, IsFin, Headers)
catch _:_ ->
terminate(State, {connection_error, compression_error,
'Error while trying to decode HPACK-encoded header block. (RFC7540 4.3)'})
end.
+stream_enforce_concurrency_limit(State=#state{opts=Opts, streams=Streams},
+ StreamID, IsFin, Headers) ->
+ MaxConcurrentStreams = maps:get(max_concurrent_streams, Opts, infinity),
+ case length(Streams) < MaxConcurrentStreams of
+ true ->
+ stream_pseudo_headers_init(State, StreamID, IsFin, Headers);
+ false ->
+ stream_refused(State, StreamID,
+ 'Maximum number of concurrent streams has been reached. (RFC7540 5.1.2)')
+ end.
+
stream_pseudo_headers_init(State=#state{local_settings=LocalSettings},
StreamID, IsFin, Headers0) ->
IsExtendedConnectEnabled = maps:get(enable_connect_protocol, LocalSettings, false),
@@ -1045,6 +1058,10 @@ stream_malformed(State=#state{socket=Socket, transport=Transport}, StreamID, _)
Transport:send(Socket, cow_http2:rst_stream(StreamID, protocol_error)),
State.
+stream_refused(State=#state{socket=Socket, transport=Transport}, StreamID, _) ->
+ Transport:send(Socket, cow_http2:rst_stream(StreamID, refused_stream)),
+ State.
+
stream_early_error(State0=#state{ref=Ref, opts=Opts, peer=Peer,
local_settings=#{initial_window_size := RemoteWindow},
remote_settings=#{initial_window_size := LocalWindow},