aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_http.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2024-01-05 16:24:25 +0100
committerLoïc Hoguin <[email protected]>2024-01-05 16:32:59 +0100
commit6ef79ae410d9bce15a361303ec283f6381965404 (patch)
tree050db233e9df42e421e96f6df0ff4897dc6d8ec4 /src/cowboy_http.erl
parent5b2f600036145653c48a7e8a60853e4a0ecc770b (diff)
downloadcowboy-6ef79ae410d9bce15a361303ec283f6381965404.tar.gz
cowboy-6ef79ae410d9bce15a361303ec283f6381965404.tar.bz2
cowboy-6ef79ae410d9bce15a361303ec283f6381965404.zip
Reject HTTP/1 requests with both content-length and transfer-encoding
The previous behavior was to accept them and drop the content-length header as per the RFC recommendation. But since this behavior is not normal it is safer to just reject such requests than risk security issues.
Diffstat (limited to 'src/cowboy_http.erl')
-rw-r--r--src/cowboy_http.erl23
1 files changed, 13 insertions, 10 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index bba2108..70f8268 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -765,39 +765,42 @@ default_port(_) -> 80.
request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, sock=Sock, cert=Cert,
proxy_header=ProxyHeader, in_streamid=StreamID, in_state=
PS=#ps_header{method=Method, path=Path, qs=Qs, version=Version}},
- Headers0, Host, Port) ->
+ Headers, Host, Port) ->
Scheme = case Transport:secure() of
true -> <<"https">>;
false -> <<"http">>
end,
- {Headers, HasBody, BodyLength, TDecodeFun, TDecodeState} = case Headers0 of
+ {HasBody, BodyLength, TDecodeFun, TDecodeState} = case Headers of
+ #{<<"transfer-encoding">> := _, <<"content-length">> := _} ->
+ error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}},
+ {stream_error, protocol_error,
+ 'The request had both transfer-encoding and content-length headers. (RFC7230 3.3.3)'});
#{<<"transfer-encoding">> := TransferEncoding0} ->
try cow_http_hd:parse_transfer_encoding(TransferEncoding0) of
[<<"chunked">>] ->
- {maps:remove(<<"content-length">>, Headers0),
- true, undefined, fun cow_http_te:stream_chunked/2, {0, 0}};
+ {true, undefined, fun cow_http_te:stream_chunked/2, {0, 0}};
_ ->
- error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers0}},
+ error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}},
{stream_error, protocol_error,
'Cowboy only supports transfer-encoding: chunked. (RFC7230 3.3.1)'})
catch _:_ ->
- error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers0}},
+ error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}},
{stream_error, protocol_error,
'The transfer-encoding header is invalid. (RFC7230 3.3.1)'})
end;
#{<<"content-length">> := <<"0">>} ->
- {Headers0, false, 0, undefined, undefined};
+ {false, 0, undefined, undefined};
#{<<"content-length">> := BinLength} ->
Length = try
cow_http_hd:parse_content_length(BinLength)
catch _:_ ->
- error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers0}},
+ error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}},
{stream_error, protocol_error,
'The content-length header is invalid. (RFC7230 3.3.2)'})
end,
- {Headers0, true, Length, fun cow_http_te:stream_identity/2, {0, Length}};
+ {true, Length, fun cow_http_te:stream_identity/2, {0, Length}};
_ ->
- {Headers0, false, 0, undefined, undefined}
+ {false, 0, undefined, undefined}
end,
Req0 = #{
ref => Ref,