diff options
author | Loïc Hoguin <[email protected]> | 2018-02-28 16:18:29 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2018-02-28 16:18:29 +0100 |
commit | 19054e40e04e3f3012963c3229033c69d4973388 (patch) | |
tree | e5cbd2b9b97a47d6c4b4a8cd2acccfb1973586a7 | |
parent | e23e12287ab0557444c676c8f6c3c27db3427659 (diff) | |
download | cowboy-19054e40e04e3f3012963c3229033c69d4973388.tar.gz cowboy-19054e40e04e3f3012963c3229033c69d4973388.tar.bz2 cowboy-19054e40e04e3f3012963c3229033c69d4973388.zip |
Fix crash in cowboy_http2 when content-length is invalid
-rw-r--r-- | src/cowboy_http2.erl | 25 | ||||
-rw-r--r-- | test/rfc7540_SUITE.erl | 20 |
2 files changed, 33 insertions, 12 deletions
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index 54cd5c8..6399346 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -927,24 +927,27 @@ headers_to_map([{Name, Value}|Tail], Acc0) -> end, headers_to_map(Tail, Acc). -stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert}, - StreamID, IsFin, Headers, #{method := Method, scheme := Scheme, - authority := Authority, path := PathWithQs}) -> - BodyLength = case Headers of +stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders) -> + case Headers of _ when IsFin =:= fin -> - 0; + stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, 0); #{<<"content-length">> := <<"0">>} -> - 0; + stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, 0); #{<<"content-length">> := BinLength} -> try - cow_http_hd:parse_content_length(BinLength) + stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, + cow_http_hd:parse_content_length(BinLength)) catch _:_ -> - terminate(State, {stream_error, StreamID, protocol_error, - 'The content-length header is invalid. (RFC7230 3.3.2)'}) + stream_malformed(State, StreamID, + 'The content-length header is invalid. (RFC7230 3.3.2)') end; _ -> - undefined - end, + stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, undefined) + end. + +stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert}, + StreamID, IsFin, Headers, #{method := Method, scheme := Scheme, + authority := Authority, path := PathWithQs}, BodyLength) -> try cow_http_hd:parse_host(Authority) of {Host, Port} -> try cow_http:parse_fullpath(PathWithQs) of diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl index fdd83b3..a97da71 100644 --- a/test/rfc7540_SUITE.erl +++ b/test/rfc7540_SUITE.erl @@ -3196,7 +3196,25 @@ reject_many_pseudo_header_path(Config) -> % that is defined to have no payload, as described in [RFC7230], % Section 3.3.2, can have a non-zero content-length header field, even % though no content is included in DATA frames. -% + +reject_duplicate_content_length_header(Config) -> + doc("A request with duplicate content-length headers must be rejected " + "with a PROTOCOL_ERROR stream error. (RFC7230 3.3.2, RFC7540 8.1.2.6)"), + {ok, Socket} = do_handshake(Config), + %% Send a HEADERS frame with more than one content-length header. + {HeadersBlock, _} = cow_hpack:encode([ + {<<":method">>, <<"GET">>}, + {<<":scheme">>, <<"http">>}, + {<<":authority">>, <<"localhost">>}, %% @todo Correct port number. + {<<":path">>, <<>>}, + {<<"content-length">>, <<"12">>}, + {<<"content-length">>, <<"12">>} + ]), + ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)), + %% Receive a PROTOCOL_ERROR stream error. + {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000), + ok. + % Intermediaries that process HTTP requests or responses (i.e., any % intermediary not acting as a tunnel) MUST NOT forward a malformed % request or response. Malformed requests or responses that are |