mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Add more rfc7230 tests
Also fixes the handling of the max_headers option for HTTP/1.1. It is now a strict limit and not dependent on whether data is already in the buffer.
This commit is contained in:
parent
d7761b5259
commit
62bf505d33
2 changed files with 103 additions and 36 deletions
|
@ -454,18 +454,24 @@ parse_header(Rest, State=#state{in_state=PS}, Headers) when byte_size(Rest) < 2
|
|||
parse_header(<< $\r, $\n, Rest/bits >>, S, Headers) ->
|
||||
request(Rest, S, Headers);
|
||||
parse_header(Buffer, State=#state{opts=Opts, in_state=PS}, Headers) ->
|
||||
MaxLength = maps:get(max_header_name_length, Opts, 64),
|
||||
MaxHeaders = maps:get(max_headers, Opts, 100),
|
||||
NumHeaders = maps:size(Headers),
|
||||
if
|
||||
NumHeaders >= MaxHeaders ->
|
||||
error_terminate(431, State#state{in_state=PS#ps_header{headers=Headers}},
|
||||
{connection_error, limit_reached,
|
||||
'The number of headers is larger than configuration allows. (RFC7230 3.2.5, RFC6585 5)'});
|
||||
true ->
|
||||
parse_header_colon(Buffer, State, Headers)
|
||||
end.
|
||||
|
||||
parse_header_colon(Buffer, State=#state{opts=Opts, in_state=PS}, Headers) ->
|
||||
MaxLength = maps:get(max_header_name_length, Opts, 64),
|
||||
case match_colon(Buffer, 0) of
|
||||
nomatch when byte_size(Buffer) > MaxLength ->
|
||||
error_terminate(431, State#state{in_state=PS#ps_header{headers=Headers}},
|
||||
{connection_error, limit_reached,
|
||||
'A header name is larger than configuration allows. (RFC7230 3.2.5, RFC6585 5)'});
|
||||
nomatch when NumHeaders >= MaxHeaders ->
|
||||
error_terminate(431, State#state{in_state=PS#ps_header{headers=Headers}},
|
||||
{connection_error, limit_reached,
|
||||
'The number of headers is larger than configuration allows. (RFC7230 3.2.5, RFC6585 5)'});
|
||||
nomatch ->
|
||||
{more, State#state{in_state=PS#ps_header{headers=Headers}}, Buffer};
|
||||
_ ->
|
||||
|
|
|
@ -711,21 +711,47 @@ reject_invalid_whitespace_after_version(Config) ->
|
|||
%
|
||||
%OWS = *( SP / HTAB )
|
||||
%```
|
||||
%
|
||||
%lower_case_header(Config) ->
|
||||
%upper_case_header(Config) ->
|
||||
%mixed_case_header(Config) ->
|
||||
%The header field name is case insensitive. (RFC7230 3.2)
|
||||
%
|
||||
%reject_whitespace_before_header_name(Config) ->
|
||||
%Messages that contain whitespace before the header name must
|
||||
%be rejected with a 400 status code and the closing of the
|
||||
%connection. (RFC7230 3.2.4)
|
||||
%
|
||||
%reject_whitespace_between_header_name_and_colon(Config) ->
|
||||
%Messages that contain whitespace between the header name and
|
||||
%colon must be rejected with a 400 status code and the closing
|
||||
%of the connection. (RFC7230 3.2.4)
|
||||
|
||||
lower_case_header(Config) ->
|
||||
doc("The header field name is case insensitive. (RFC7230 3.2)"),
|
||||
#{code := 200} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"host: localhost\r\n"
|
||||
"\r\n"]).
|
||||
|
||||
upper_case_header(Config) ->
|
||||
doc("The header field name is case insensitive. (RFC7230 3.2)"),
|
||||
#{code := 200} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"HOST: localhost\r\n"
|
||||
"\r\n"]).
|
||||
|
||||
mixed_case_header(Config) ->
|
||||
doc("The header field name is case insensitive. (RFC7230 3.2)"),
|
||||
#{code := 200} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"hOsT: localhost\r\n"
|
||||
"\r\n"]).
|
||||
|
||||
reject_whitespace_before_header_name(Config) ->
|
||||
doc("Messages that contain whitespace before the header name must "
|
||||
"be rejected with a 400 status code and the closing of the "
|
||||
"connection. (RFC7230 3.2.4)"),
|
||||
#{code := 400, client := Client} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
" Host: localhost\r\n"
|
||||
"\r\n"]),
|
||||
{error, closed} = raw_recv(Client, 0, 1000).
|
||||
|
||||
reject_whitespace_between_header_name_and_colon(Config) ->
|
||||
doc("Messages that contain whitespace between the header name and "
|
||||
"colon must be rejected with a 400 status code and the closing "
|
||||
"of the connection. (RFC7230 3.2.4)"),
|
||||
#{code := 400, client := Client} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host : localhost\r\n"
|
||||
"\r\n"]),
|
||||
{error, closed} = raw_recv(Client, 0, 1000).
|
||||
|
||||
limit_header_name(Config) ->
|
||||
doc("The header name must be subject to a configurable limit. A "
|
||||
|
@ -753,11 +779,26 @@ limit_header_value(Config) ->
|
|||
"\r\n"]),
|
||||
{error, closed} = raw_recv(Client, 0, 1000).
|
||||
|
||||
%drop_whitespace_before_header_value(Config) ->
|
||||
%drop_whitespace_after_header_value(Config) ->
|
||||
%Optional whitespace before and after the header value is not
|
||||
%part of the value and must be dropped.
|
||||
%
|
||||
drop_whitespace_before_header_value(Config) ->
|
||||
doc("Optional whitespace before and after the header value is not "
|
||||
"part of the value and must be dropped."),
|
||||
#{code := 200} = do_raw(Config, [
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"Content-length: \t 12\r\n"
|
||||
"\r\n"
|
||||
"Hello world!"]).
|
||||
|
||||
drop_whitespace_after_header_value(Config) ->
|
||||
doc("Optional whitespace before and after the header value is not "
|
||||
"part of the value and must be dropped."),
|
||||
#{code := 200} = do_raw(Config, [
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"Content-length: 12 \t \r\n"
|
||||
"\r\n"
|
||||
"Hello world!"]).
|
||||
|
||||
%@todo
|
||||
%The order of header fields with differing names is not significant. (RFC7230 3.2.2)
|
||||
%
|
||||
|
@ -777,10 +818,17 @@ reject_duplicate_content_length_header(Config) ->
|
|||
"Hello world!"]),
|
||||
{error, closed} = raw_recv(Client, 0, 1000).
|
||||
|
||||
%reject_duplicate_host_header(Config) ->
|
||||
%Requests with duplicate content-length or host headers must be rejected
|
||||
%with a 400 status code and the closing of the connection. (RFC7230 3.3.2)
|
||||
%
|
||||
reject_duplicate_host_header(Config) ->
|
||||
doc("Requests with duplicate host headers must be rejected "
|
||||
"with a 400 status code and the closing of the connection. (RFC7230 3.3.2)"),
|
||||
#{code := 400, client := Client} = do_raw(Config, [
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"\r\n"
|
||||
"Hello world!"]),
|
||||
{error, closed} = raw_recv(Client, 0, 1000).
|
||||
|
||||
%combine_duplicate_headers(Config) ->
|
||||
%Other duplicate header fields must be combined by inserting a comma
|
||||
%between the values in the order they were received. (RFC7230 3.2.2)
|
||||
|
@ -792,13 +840,26 @@ reject_duplicate_content_length_header(Config) ->
|
|||
%
|
||||
%wait_for_eoh_before_processing_request(Config) ->
|
||||
%The request must not be processed until all headers have arrived. (RFC7230 3.2.2)
|
||||
%
|
||||
%limit_headers(Config) ->
|
||||
%The number of headers allowed in a request must be subject to
|
||||
%a configurable limit. There is no recommendations for the default.
|
||||
%100 headers is known to work well. Such a request must be rejected
|
||||
%with a 431 status code and the closing of the connection. (RFC7230 3.2.5, RFC6585 5)
|
||||
%
|
||||
|
||||
limit_headers(Config) ->
|
||||
doc("The number of headers allowed in a request must be subject to "
|
||||
"a configurable limit. There is no recommendations for the default. "
|
||||
"100 headers is known to work well. Such a request must be rejected "
|
||||
"with a 431 status code and the closing of the connection. (RFC7230 3.2.5, RFC6585 5)"),
|
||||
%% 100 headers.
|
||||
#{code := 200} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n",
|
||||
[["H-", integer_to_list(N), ": value\r\n"] || N <- lists:seq(1, 99)],
|
||||
"\r\n"]),
|
||||
%% 101 headers.
|
||||
#{code := 431, client := Client} = do_raw(Config, [
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n",
|
||||
[["H-", integer_to_list(N), ": value\r\n"] || N <- lists:seq(1, 100)],
|
||||
"\r\n"]),
|
||||
{error, closed} = raw_recv(Client, 0, 1000).
|
||||
|
||||
%@todo
|
||||
%When parsing header field values, the server must ignore empty
|
||||
%list elements, and not count those as the count of elements present. (RFC7230 7)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue