Skip to content

Commit 6ff6aba

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 6ff6aba

File tree

2 files changed

+56
-15
lines changed

2 files changed

+56
-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: 46 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_headers_with_body/0,
24+
fun test_post_includes_content_headers_with_empty_body/0,
25+
fun test_get_includes_content_headers_with_body/0,
26+
fun test_get_excludes_content_headers_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,48 @@ 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_headers_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+
?assertEqual(<<"application/octet-stream">>, proplists:get_value(<<"Content-Type">>, ReqHeaders)).
202+
203+
test_post_includes_content_headers_with_empty_body() ->
204+
URL = <<"http://localhost:8000/post">>,
205+
Body = <<>>,
206+
Options = [with_body],
207+
{ok, 200, _H, JsonBody} = hackney:post(URL, [], Body, Options),
208+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
209+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
210+
?assertEqual(<<"0">>, proplists:get_value(<<"Content-Length">>, ReqHeaders)),
211+
?assertEqual(<<"application/octet-stream">>, proplists:get_value(<<"Content-Type">>, ReqHeaders)).
212+
213+
test_get_includes_content_headers_with_body() ->
214+
URL = <<"http://localhost:8000/get">>,
215+
Body = <<"{\"test\": \"ok\" }">>,
216+
Options = [with_body],
217+
{ok, 200, _H, JsonBody} = hackney:get(URL, [], Body, Options),
218+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
219+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
220+
?assertEqual(<<"15">>, proplists:get_value(<<"Content-Length">>, ReqHeaders)),
221+
?assertEqual(<<"application/octet-stream">>, proplists:get_value(<<"Content-Type">>, ReqHeaders)).
222+
223+
test_get_excludes_content_headers_with_empty_body() ->
224+
URL = <<"http://localhost:8000/get">>,
225+
EmptyBodies = [<<>>, [], [<<>>]],
226+
Options = [with_body],
227+
lists:foreach(fun(Body) ->
228+
{ok, 200, _H, JsonBody} = hackney:get(URL, [], Body, Options),
229+
Obj = jsone:decode(JsonBody, [{object_format, proplist}]),
230+
ReqHeaders = proplists:get_value(<<"headers">>, Obj),
231+
?assertEqual(undefined, proplists:get_value(<<"Content-Type">>, ReqHeaders)),
232+
?assertEqual(undefined, proplists:get_value(<<"Content-Length">>, ReqHeaders))
233+
end, EmptyBodies).
234+
189235
test_frees_manager_ets_when_body_is_in_client() ->
190236
URL = <<"http://localhost:8000/get">>,
191237
BeforeCount = ets:info(hackney_manager_refs, size),

0 commit comments

Comments
 (0)