Skip to content

Commit 011f3e8

Browse files
committed
Handle iolist request bodies as binaries
Fixes non-POST/PUT requests with a body of [<<>>] receiving Content-Type and Content-Length headers despite not having a body.
1 parent 436f7b2 commit 011f3e8

File tree

2 files changed

+53
-15
lines changed

2 files changed

+53
-15
lines changed

src/hackney_request.erl

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,14 @@ perform(Client0, {Method0, Path0, Headers0, Body0}) ->
8686
%% get expect headers
8787
Expect = expectation(Headers2),
8888

89+
%% convert iolist body to binary
90+
Body1 = case is_list(Body0) of
91+
true -> iolist_to_binary(Body0);
92+
false -> Body0
93+
end,
94+
8995
%% build headers with the body.
90-
{FinalHeaders, ReqType, Body, Client1} = case Body0 of
96+
{FinalHeaders, ReqType, Body, Client1} = case Body1 of
9197
stream ->
9298
{Headers2, ReqType0, stream, Client0};
9399
stream_multipart ->
@@ -98,15 +104,11 @@ perform(Client0, {Method0, Path0, Headers0, Body0}) ->
98104
handle_multipart_body(Headers2, ReqType0,
99105
Size, Boundary, Client0);
100106
<<>> when Method =:= <<"POST">> orelse Method =:= <<"PUT">> ->
101-
handle_body(Headers2, ReqType0, Body0, Client0);
102-
[] when Method =:= <<"POST">> orelse Method =:= <<"PUT">> ->
103-
handle_body(Headers2, ReqType0, Body0, Client0);
107+
handle_body(Headers2, ReqType0, Body1, Client0);
104108
<<>> ->
105-
{Headers2, ReqType0, Body0, Client0};
106-
[] ->
107-
{Headers2, ReqType0, Body0, Client0};
109+
{Headers2, ReqType0, Body1, Client0};
108110
_ ->
109-
handle_body(Headers2, ReqType0, Body0, Client0)
111+
handle_body(Headers2, ReqType0, Body1, Client0)
110112
end,
111113

112114
%% build final client record
@@ -370,13 +372,6 @@ handle_body(Headers, ReqType0, Body0, Client) ->
370372
S = hackney_headers_new:get_value(<<"content-length">>, Headers),
371373
{S, CT, Body0};
372374

373-
_ when is_list(Body0) -> % iolist case
374-
Body1 = iolist_to_binary(Body0),
375-
S = erlang:byte_size(Body1),
376-
CT = hackney_headers_new:get_value(
377-
<<"content-type">>, Headers, <<"application/octet-stream">>
378-
),
379-
{S, CT, Body1};
380375
_ when is_binary(Body0) ->
381376
S = erlang:byte_size(Body0),
382377
CT = hackney_headers_new:get_value(

test/hackney_integration_tests.erl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ all_tests() ->
2020
% fun relative_redirect_request_no_follow/0,
2121
fun relative_redirect_request_follow/0,
2222
fun test_duplicate_headers/0,
23+
fun test_post_includes_content_length_with_body/0,
24+
fun test_post_includes_content_length_with_empty_body/0,
25+
fun test_get_includes_content_length_with_body/0,
26+
fun test_get_excludes_content_length_with_empty_body/0,
2327
fun test_custom_host_headers/0,
2428
fun async_request/0,
2529
fun async_head_request/0,
@@ -186,6 +190,45 @@ test_custom_host_headers() ->
186190
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
187191
?assertEqual(<<"myhost.com">>, proplists:get_value(<<"Host">>, ReqHeaders)).
188192

193+
test_post_includes_content_length_with_body() ->
194+
URL = <<"http://localhost:8000/post">>,
195+
Body = <<"{\"test\": \"ok\" }">>,
196+
Options = [with_body],
197+
{ok, 200, _H, JsonBody} = hackney:post(URL, [], Body, Options),
198+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
199+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
200+
?assertEqual(<<"15">>, proplists:get_value(<<"Content-Length">>, ReqHeaders)).
201+
202+
test_post_includes_content_length_with_empty_body() ->
203+
URL = <<"http://localhost:8000/post">>,
204+
Body = <<>>,
205+
Options = [with_body],
206+
{ok, 200, _H, JsonBody} = hackney:post(URL, [], Body, Options),
207+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
208+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
209+
?assertEqual(<<"0">>, proplists:get_value(<<"Content-Length">>, ReqHeaders)).
210+
211+
test_get_includes_content_length_with_body() ->
212+
URL = <<"http://localhost:8000/post">>,
213+
Body = <<"{\"test\": \"ok\" }">>,
214+
Options = [with_body],
215+
{ok, 200, _H, JsonBody} = hackney:post(URL, [], Body, Options),
216+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
217+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
218+
?assertEqual(<<"15">>, proplists:get_value(<<"Content-Length">>, ReqHeaders)).
219+
220+
test_get_excludes_content_length_with_empty_body() ->
221+
URL = <<"http://localhost:8000/get">>,
222+
EmptyBodies = [<<>>, [], [<<>>]],
223+
Options = [with_body],
224+
lists:foreach(fun(Body) ->
225+
{ok, 200, _H, JsonBody} = hackney:get(URL, [], Body, Options),
226+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
227+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
228+
?assertEqual(undefined, proplists:get_value(<<"Content-Type">>, ReqHeaders)),
229+
?assertEqual(undefined, proplists:get_value(<<"Content-Length">>, ReqHeaders))
230+
end, EmptyBodies).
231+
189232
test_frees_manager_ets_when_body_is_in_client() ->
190233
URL = <<"http://localhost:8000/get">>,
191234
BeforeCount = ets:info(hackney_manager_refs, size),

0 commit comments

Comments
 (0)