diff options
author | Péter Dimitrov <[email protected]> | 2018-10-02 16:26:59 +0200 |
---|---|---|
committer | Péter Dimitrov <[email protected]> | 2018-10-02 16:26:59 +0200 |
commit | bb5ba03ac8ada0b05c95c7cde295e61fe3faca5c (patch) | |
tree | d6662eda32de291d0b6296cfdf53d252c92abfac | |
parent | 0edd7ee4d575656f6da6558e40d6993f41a4be38 (diff) | |
download | otp-bb5ba03ac8ada0b05c95c7cde295e61fe3faca5c.tar.gz otp-bb5ba03ac8ada0b05c95c7cde295e61fe3faca5c.tar.bz2 otp-bb5ba03ac8ada0b05c95c7cde295e61fe3faca5c.zip |
inets: Fix handling of 'Content-Length' (httpc)
RFC7230 3.3.2
A sender MUST NOT send a 'Content-Length' header field in any message
that contains a 'Transfer-Encoding' header field.
Change-Id: I568fc65cafe3ab30baad56c002459ff74e4dbb28
-rw-r--r-- | lib/inets/src/http_client/httpc_request.erl | 11 | ||||
-rw-r--r-- | lib/inets/test/httpc_SUITE.erl | 75 |
2 files changed, 73 insertions, 13 deletions
diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index 9b81bd7a80..9db961cf9e 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -221,7 +221,8 @@ update_headers(Headers, ContentType, Body, []) -> %% that does not include a message body. This implies that either the %% Content-Length or the Transfer-Encoding header MUST be present. %% DO NOT send content-type when Body is empty. - Headers#http_request_h{'content-type' = ContentType}; + Headers1 = Headers#http_request_h{'content-type' = ContentType}, + handle_transfer_encoding(Headers1); _ -> Headers#http_request_h{ 'content-length' = body_length(Body), @@ -230,6 +231,14 @@ update_headers(Headers, ContentType, Body, []) -> update_headers(_, _, _, HeadersAsIs) -> HeadersAsIs. +handle_transfer_encoding(Headers = #http_request_h{'transfer-encoding' = undefined}) -> + Headers; +handle_transfer_encoding(Headers) -> + %% RFC7230 3.3.2 + %% A sender MUST NOT send a 'Content-Length' header field in any message + %% that contains a 'Transfer-Encoding' header field. + Headers#http_request_h{'content-length' = undefined}. + body_length(Body) when is_binary(Body) -> integer_to_list(size(Body)); diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 3d375222b5..41f8b60363 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -170,7 +170,8 @@ misc() -> server_does_not_exist, timeout_memory_leak, wait_for_whole_response, - post_204_chunked + post_204_chunked, + chunkify_fun ]. sim_mixed() -> @@ -1408,7 +1409,8 @@ post_204_chunked(_Config) -> {ok, ListenSocket} = gen_tcp:listen(0, [{active,once}, binary]), {ok,{_,Port}} = inet:sockname(ListenSocket), - spawn(fun () -> custom_server(Msg, Chunk, ListenSocket) end), + spawn(fun () -> custom_server(Msg, Chunk, ListenSocket, + fun post_204_receive/0) end), {ok,Host} = inet:gethostname(), End = "/cgi-bin/erl/httpd_example:post_204", @@ -1418,16 +1420,26 @@ post_204_chunked(_Config) -> %% Second request times out in the faulty case. {ok, _} = httpc:request(post, {URL, [], "text/html", []}, [], []). -custom_server(Msg, Chunk, ListenSocket) -> +post_204_receive() -> + receive + {tcp, _, Msg} -> + ct:log("Message received: ~p", [Msg]) + after + 1000 -> + ct:fail("Timeout: did not recive packet") + end. + +%% Custom server is used to test special cases when using chunked encoding +custom_server(Msg, Chunk, ListenSocket, ReceiveFun) -> {ok, Accept} = gen_tcp:accept(ListenSocket), - receive_packet(), + ReceiveFun(), send_response(Msg, Chunk, Accept), - custom_server_loop(Msg, Chunk, Accept). + custom_server_loop(Msg, Chunk, Accept, ReceiveFun). -custom_server_loop(Msg, Chunk, Accept) -> - receive_packet(), +custom_server_loop(Msg, Chunk, Accept, ReceiveFun) -> + ReceiveFun(), send_response(Msg, Chunk, Accept), - custom_server_loop(Msg, Chunk, Accept). + custom_server_loop(Msg, Chunk, Accept, ReceiveFun). send_response(Msg, Chunk, Socket) -> inet:setopts(Socket, [{active, once}]), @@ -1435,15 +1447,54 @@ send_response(Msg, Chunk, Socket) -> timer:sleep(250), gen_tcp:send(Socket, Chunk). -receive_packet() -> +%%-------------------------------------------------------------------- +chunkify_fun() -> + [{doc,"Test that a chunked encoded request does not include the 'Content-Length header'"}]. +chunkify_fun(_Config) -> + Msg = "HTTP/1.1 204 No Content\r\n" ++ + "Date: Thu, 23 Aug 2018 13:36:29 GMT\r\n" ++ + "Content-Type: text/html\r\n" ++ + "Server: inets/6.5.2.3\r\n" ++ + "Cache-Control: no-cache\r\n" ++ + "Pragma: no-cache\r\n" ++ + "Expires: Fri, 24 Aug 2018 07:49:35 GMT\r\n" ++ + "Transfer-Encoding: chunked\r\n" ++ + "\r\n", + Chunk = "0\r\n\r\n", + + {ok, ListenSocket} = gen_tcp:listen(0, [{active,once}, binary]), + {ok,{_,Port}} = inet:sockname(ListenSocket), + spawn(fun () -> custom_server(Msg, Chunk, ListenSocket, + fun chunkify_receive/0) end), + + {ok,Host} = inet:gethostname(), + End = "/cgi-bin/erl/httpd_example", + URL = ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End, + Fun = fun(_) -> {ok,<<1>>,eof_body} end, + Acc = start, + + {ok, {{_,204,_}, _, _}} = + httpc:request(put, {URL, [], "text/html", {chunkify, Fun, Acc}}, [], []). + +chunkify_receive() -> + Error = "HTTP/1.1 500 Internal Server Error\r\n" ++ + "Content-Length: 0\r\n\r\n", receive - {tcp, _, Msg} -> - ct:log("Message received: ~p", [Msg]) + {tcp, Port, Msg} -> + case binary:match(Msg, <<"content-length">>) of + nomatch -> + ct:log("Message received: ~s", [binary_to_list(Msg)]); + {_, _} -> + ct:log("Message received (negative): ~s", [binary_to_list(Msg)]), + %% Signal a testcase failure when the received HTTP request + %% contains a 'Content-Length' header. + gen_tcp:send(Port, Error), + ct:fail("Content-Length present in received headers.") + end after 1000 -> ct:fail("Timeout: did not recive packet") end. - %%-------------------------------------------------------------------- stream_fun_server_close() -> [{doc, "Test that an error msg is received when using a receiver fun as stream target"}]. |