diff options
author | Loïc Hoguin <[email protected]> | 2018-11-14 19:24:39 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2018-11-14 19:24:39 +0100 |
commit | 292039362a6125dfd0a163d5b0a49b800bf80b11 (patch) | |
tree | 31a38f67960503ece6abfa82a0d285152d8dc4ff | |
parent | 4fedb336311604253153eafe81f33a77b44a0b2f (diff) | |
download | cowboy-292039362a6125dfd0a163d5b0a49b800bf80b11.tar.gz cowboy-292039362a6125dfd0a163d5b0a49b800bf80b11.tar.bz2 cowboy-292039362a6125dfd0a163d5b0a49b800bf80b11.zip |
Don't send the content-length header in empty 304 responses
It's OK to send it when set explicitly, as it can be set
to what the representation's size would have been.
-rw-r--r-- | src/cowboy_http.erl | 2 | ||||
-rw-r--r-- | src/cowboy_req.erl | 8 | ||||
-rw-r--r-- | test/handlers/resp_h.erl | 3 | ||||
-rw-r--r-- | test/rfc7230_SUITE.erl | 66 |
4 files changed, 74 insertions, 5 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index caa4932..56a4af8 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -963,6 +963,8 @@ commands(State0=#state{socket=Socket, transport=Transport, streams=Streams0, out {State1, Headers1} = case {Status, ContentLength, Version} of {204, _, 'HTTP/1.1'} -> {State0#state{out_state=done}, Headers0}; + {304, _, 'HTTP/1.1'} -> + {State0#state{out_state=done}, Headers0}; {_, undefined, 'HTTP/1.1'} -> {State0#state{out_state=chunked}, Headers0#{<<"transfer-encoding">> => <<"chunked">>}}; %% Close the connection after streaming without content-length to HTTP/1.0 client. diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl index 2c3de06..5633182 100644 --- a/src/cowboy_req.erl +++ b/src/cowboy_req.erl @@ -771,11 +771,15 @@ reply(Status, Headers, SendFile = {sendfile, _, Len, _}, Req) do_reply(Status, Headers#{ <<"content-length">> => integer_to_binary(Len) }, SendFile, Req); -%% 204 responses must not include content-length. (RFC7230 3.3.1, RFC7230 3.3.2) -reply(Status=204, Headers, Body, Req) -> +%% 204 responses must not include content-length. 304 responses may +%% but only when set explicitly. (RFC7230 3.3.1, RFC7230 3.3.2) +reply(Status, Headers, Body, Req) + when Status =:= 204; Status =:= 304 -> do_reply(Status, Headers, Body, Req); reply(Status= <<"204",_/bits>>, Headers, Body, Req) -> do_reply(Status, Headers, Body, Req); +reply(Status= <<"304",_/bits>>, Headers, Body, Req) -> + do_reply(Status, Headers, Body, Req); reply(Status, Headers, Body, Req) when is_integer(Status); is_binary(Status) -> do_reply(Status, Headers#{ diff --git a/test/handlers/resp_h.erl b/test/handlers/resp_h.erl index 7a80e75..1c587ce 100644 --- a/test/handlers/resp_h.erl +++ b/test/handlers/resp_h.erl @@ -186,6 +186,9 @@ do(<<"stream_reply2">>, Req0, Opts) -> <<"204">> -> Req = cowboy_req:stream_reply(204, Req0), {ok, Req, Opts}; + <<"304">> -> + Req = cowboy_req:stream_reply(304, Req0), + {ok, Req, Opts}; Status -> Req = cowboy_req:stream_reply(binary_to_integer(Status), Req0), stream_body(Req), diff --git a/test/rfc7230_SUITE.erl b/test/rfc7230_SUITE.erl index 3e06d6c..6887d27 100644 --- a/test/rfc7230_SUITE.erl +++ b/test/rfc7230_SUITE.erl @@ -1745,9 +1745,55 @@ no_body_in_head_response(Config) -> %no_body_in_100_response(Config) -> %no_body_in_101_response(Config) -> %no_body_in_102_response(Config) -> -%no_body_in_204_response(Config) -> -%no_body_in_304_response(Config) -> -%1xx, 204 and 304 responses never include a message body. (RFC7230 3.3) +%1xx responses never include a message body. (RFC7230 3.3) + +no_body_in_204_response(Config) -> + doc("204 responses never include a message body. (RFC7230 3.3)"), + Client = raw_open(Config), + ok = raw_send(Client, [ + "GET /resp/reply2/204 HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"]), + {_, 204, _, Rest} = cow_http:parse_status_line(raw_recv_head(Client)), + {_, <<>>} = cow_http:parse_headers(Rest), + {error, timeout} = raw_recv(Client, 1, 1000), + ok. + +no_body_in_204_response_stream(Config) -> + doc("204 responses never include a message body. (RFC7230 3.3)"), + Client = raw_open(Config), + ok = raw_send(Client, [ + "GET /resp/stream_reply2/204 HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"]), + {_, 204, _, Rest} = cow_http:parse_status_line(raw_recv_head(Client)), + {_, <<>>} = cow_http:parse_headers(Rest), + {error, timeout} = raw_recv(Client, 1, 1000), + ok. + +no_body_in_304_response(Config) -> + doc("304 responses never include a message body. (RFC7230 3.3)"), + Client = raw_open(Config), + ok = raw_send(Client, [ + "GET /resp/reply2/304 HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"]), + {_, 304, _, Rest} = cow_http:parse_status_line(raw_recv_head(Client)), + {_, <<>>} = cow_http:parse_headers(Rest), + {error, timeout} = raw_recv(Client, 1, 1000), + ok. + +no_body_in_304_response_stream(Config) -> + doc("304 responses never include a message body. (RFC7230 3.3)"), + Client = raw_open(Config), + ok = raw_send(Client, [ + "GET /resp/stream_reply2/304 HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"]), + {_, 304, _, Rest} = cow_http:parse_status_line(raw_recv_head(Client)), + {_, <<>>} = cow_http:parse_headers(Rest), + {error, timeout} = raw_recv(Client, 1, 1000), + ok. same_content_length_as_get_in_head_response(Config) -> doc("Responses to HEAD requests can include a content-length header. " @@ -1802,6 +1848,20 @@ no_content_length_in_204_response(Config) -> false = lists:keyfind(<<"content-length">>, 1, Headers), ok. +no_content_length_in_empty_304_response(Config) -> + doc("304 responses should not include a content-length header, " + "unless it matches the resource's and was therefore set " + "explicitly by the user. (RFC7230 3.3.1, RFC7230 3.3.2)"), + Client = raw_open(Config), + ok = raw_send(Client, [ + "GET /resp/reply3/304 HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"]), + {_, 304, _, Rest} = cow_http:parse_status_line(raw_recv_head(Client)), + {Headers, <<>>} = cow_http:parse_headers(Rest), + false = lists:keyfind(<<"content-length">>, 1, Headers), + ok. + %%% @todo CONNECT no_content_length_in_2xx_response_to_connect_request(Config) -> %no_transfer_encoding_in_100_response(Config) -> %no_transfer_encoding_in_101_response(Config) -> |