diff options
| -rw-r--r-- | doc/src/manual/cowboy_http.asciidoc | 12 | ||||
| -rw-r--r-- | src/cowboy_http.erl | 11 | ||||
| -rw-r--r-- | test/http_SUITE.erl | 67 |
3 files changed, 89 insertions, 1 deletions
diff --git a/doc/src/manual/cowboy_http.asciidoc b/doc/src/manual/cowboy_http.asciidoc index 96a5585..daa48a5 100644 --- a/doc/src/manual/cowboy_http.asciidoc +++ b/doc/src/manual/cowboy_http.asciidoc @@ -29,6 +29,8 @@ opts() :: #{ initial_stream_flow_size => non_neg_integer(), linger_timeout => timeout(), logger => module(), + max_authorization_header_value_length => non_neg_integer(), + max_cookie_header_value_length => non_neg_integer(), max_empty_lines => non_neg_integer(), max_header_name_length => non_neg_integer(), max_header_value_length => non_neg_integer(), @@ -131,6 +133,16 @@ logger (error_logger):: The module that will be used to write log messages. +max_authorization_header_value_length:: + +Maximum length of the `authorization` header value. +Defaults to the value of the option `max_header_value_length`. + +max_cookie_header_value_length:: + +Maximum length of the `cookie` header value. +Defaults to the value of the option `max_header_value_length`. + max_empty_lines (5):: Maximum number of empty lines before a request. diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index a2cf6c9..f1eabf1 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -703,7 +703,7 @@ parse_hd_before_value(<< $\s, Rest/bits >>, S, H, N) -> parse_hd_before_value(<< $\t, Rest/bits >>, S, H, N) -> parse_hd_before_value(Rest, S, H, N); parse_hd_before_value(Buffer, State=#state{opts=Opts, in_state=PS}, H, N) -> - MaxLength = maps:get(max_header_value_length, Opts, 4096), + MaxLength = max_header_value_length(N, Opts), case match_eol(Buffer, 0) of nomatch when byte_size(Buffer) > MaxLength -> error_terminate(431, State#state{in_state=PS#ps_header{headers=H}}, @@ -715,6 +715,15 @@ parse_hd_before_value(Buffer, State=#state{opts=Opts, in_state=PS}, H, N) -> parse_hd_value(Buffer, State, H, N, <<>>) end. +max_header_value_length(<<"authorization">>, #{max_authorization_header_value_length := Max}) -> + Max; +max_header_value_length(<<"cookie">>, #{max_cookie_header_value_length := Max}) -> + Max; +max_header_value_length(_, #{max_header_value_length := Max}) -> + Max; +max_header_value_length(_, _) -> + 4096. + parse_hd_value(<< $\r, $\n, Rest/bits >>, S, Headers0, Name, SoFar) -> Value = clean_value_ws_end(SoFar, byte_size(SoFar) - 1), Headers = case maps:get(Name, Headers0, undefined) of diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl index 9928136..51b4ac5 100644 --- a/test/http_SUITE.erl +++ b/test/http_SUITE.erl @@ -489,6 +489,73 @@ do_idle_timeout_recv_loop(Ref, Pid, ConnPid, StreamRef, ExpectCompletion) -> error(timeout) end. +max_authorization_header_value_length(Config) -> + doc("Confirm the max_authorization_header_value_length option " + "correctly limits the length of authorization header values."), + {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ + env => #{dispatch => init_dispatch(Config)}, + max_authorization_header_value_length => 2048 + }), + Port = ranch:get_port(?FUNCTION_NAME), + try + do_max_header_value_length(Config, Port, + <<"authorization">>, 2048), + %% Confirm that other headers still use the default limit. + do_max_header_value_length(Config, Port, + <<"my-header">>, 4096) + after + cowboy:stop_listener(?FUNCTION_NAME) + end. + +max_cookie_header_value_length(Config) -> + doc("Confirm the max_cookie_header_value_length option " + "correctly limits the length of cookie header values."), + {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ + env => #{dispatch => init_dispatch(Config)}, + max_cookie_header_value_length => 2048 + }), + Port = ranch:get_port(?FUNCTION_NAME), + try + do_max_header_value_length(Config, Port, + <<"cookie">>, 2048), + %% Confirm that other headers still use the default limit. + do_max_header_value_length(Config, Port, + <<"my-header">>, 4096) + after + cowboy:stop_listener(?FUNCTION_NAME) + end. + +max_header_value_length(Config) -> + doc("Confirm the max_header_value_length option " + "correctly limits the length of header values."), + {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ + env => #{dispatch => init_dispatch(Config)}, + max_header_value_length => 2048 + }), + Port = ranch:get_port(?FUNCTION_NAME), + try + do_max_header_value_length(Config, Port, + <<"my-header">>, 2048) + after + cowboy:stop_listener(?FUNCTION_NAME) + end. + +max_header_value_length_default(Config) -> + doc("Confirm the max_header_value_length option " + "correctly limits the length of header values."), + do_max_header_value_length(Config, config(port, Config), + <<"my-header">>, 4096). + +do_max_header_value_length(Config, Port, Name, MaxLen) -> + ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]), + {ok, http} = gun:await_up(ConnPid), + StreamRef1 = gun:get(ConnPid, "/", #{Name => lists:duplicate(MaxLen, $a)}), + {response, nofin, 200, _} = gun:await(ConnPid, StreamRef1), + %% We * 2 because this is a soft limit. + StreamRef2 = gun:get(ConnPid, "/", #{Name => lists:duplicate(MaxLen * 2, $a)}), + {response, fin, 431, _} = gun:await(ConnPid, StreamRef2), + gun:close(ConnPid). + persistent_term_router(Config) -> doc("The router can retrieve the routes from persistent_term storage."), case erlang:function_exported(persistent_term, get, 1) of |
