Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

allows the use of a custom function to stream the body if neeeded.

While the default is to diretcly send the request and fetch the status
and headers if the body is set as the atom `stream` the request and
send_request function will return {ok, Client}. Then you can use the
function `hackney:stream_request_body/2` to stream the request body and
`hackney:start_response/1` to initialize the respone.

The function `hackney:start_response/1` is waiting a Client with
theresponse state equal to the atom `waiting` .

ex:

    ReqBody = << "{
          \"id\": \"some_paste_id2\",
          \"rev\": \"some_revision_id\",
          \"changeset\": \"changeset in unidiff format\"
    }" >>,
    ReqHeaders = [{<<"Content-Type">>, <<"application/json">>}],
    Path = <<"https://friendpaste.com/">>,
    Method = post,
    {ok, Client} = hackney:request(Method, Path, ReqHeaders, stream,
                                   []),

    {ok, Client1} = hackney:stream_request_body(ReqBody, Client),
    {ok, _Status, _Headers, Client2} = hackney:start_response(Client1),
    {ok, Body, Client3} = hackney:body(Client2),
    hackney:close(Client3).
  • Loading branch information...
commit ccd58b814d79bca323e63da93b679ed42001bd9f 1 parent 9b0170c
@benoitc authored
View
24 examples/test2.erl
@@ -0,0 +1,24 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%! -pa ./ebin -pa ./deps/mimetypes/ebin
+
+-module(test2).
+
+main(_) ->
+ hackney:start(),
+ ReqBody = << "{
+ \"id\": \"some_paste_id2\",
+ \"rev\": \"some_revision_id\",
+ \"changeset\": \"changeset in unidiff format\"
+}" >>,
+ ReqHeaders = [{<<"Content-Type">>, <<"application/json">>}],
+ Path = <<"https://friendpaste.com/">>,
+ Method = post,
+ {ok, Client} = hackney:request(Method, Path, ReqHeaders, stream,
+ []),
+
+ {ok, Client1} = hackney:stream_request_body(ReqBody, Client),
+ {ok, _Status, _Headers, Client2} = hackney:start_response(Client1),
+ {ok, Body, Client3} = hackney:body(Client2),
+ hackney:close(Client3),
+ io:format(" ** got length ~p~n ** body: ~p~n", [byte_size(Body), Body]).
View
18 src/hackney.erl
@@ -14,6 +14,8 @@
start_pool/2, stop_pool/1,
request/1, request/2, request/3, request/4, request/5,
send_request/2,
+ start_response/1,
+ stream_request_body/2,
stream_body/1,
body/1, body/2, skip_body/1,
pool/1]).
@@ -143,6 +145,7 @@ request(Method, URL, Headers, Body, Options)
request(Method, hackney_url:parse_url(URL), Headers, Body, Options).
+%% @doc send a request using the current client state
send_request(#client{response_state=done}=Client0 ,
{Method, Path, Headers, Body}) ->
Client = Client0#client{response_state=on_status,
@@ -164,6 +167,21 @@ send_request(Client0, {Method, Path, Headers, Body}) ->
Error
end.
+%% @doc stream the request body. It isued after sending a request using
+%% the `request' and `send_request' functions.
+-spec stream_request_body(term(), #client{})
+ -> {ok, #client{}} | {error, term()}.
+stream_request_body(Body, Client) ->
+ hackney_request:stream_body(Body, Client).
+
+%% @doc start a response.
+%% Useful if you stream the body by yourself. It will fetch the status
+%% and headers of the response. and return
+-spec start_response(#client{})
+ -> {ok, integer(), list(), #client{}} | {error, term()}.
+start_response(Client) ->
+ hackney_response:start_response(Client).
+
%% @doc Stream the response body.
stream_body(Client) ->
View
83 src/hackney_request.erl
@@ -8,7 +8,7 @@
-include("hackney.hrl").
--export([perform/2, send/2, sendfile/2]).
+-export([perform/2, send/2, sendfile/2, stream_body/2]).
perform(Client, {Method0, Path, Headers0, Body0}) ->
@@ -33,10 +33,13 @@ perform(Client, {Method0, Path, Headers0, Body0}) ->
HeadersDict = hackney_headers:update(hackney_headers:new(DefaultHeaders),
Headers0),
+
%% build headers with the body.
{HeaderDict1, Body} = case Body0 of
+ stream ->
+ {HeadersDict, stream};
<<>> when Method =:= <<"POST">> orelse Method =:= <<"PUT">> ->
- hackney_request:handle_body(HeadersDict, Body0);
+ handle_body(HeadersDict, Body0);
<<>> ->
{HeadersDict, Body0};
_ ->
@@ -55,20 +58,56 @@ perform(Client, {Method0, Path, Headers0, Body0}) ->
%% send headers data
case hackney_request:send(Client, HeadersData) of
+ ok when Body =:= stream ->
+ {ok, Client#client{response_state=stream}};
ok ->
- %% send body
- Result = stream_body(Body, Client),
-
- case Result of
+ case stream_body(Body, Client) of
{error, _Reason}=E ->
E;
- _ ->
- hackney_response:init(Client)
+ {ok, Client1} ->
+ hackney_response:start_response(Client1)
end;
Error ->
Error
end.
+stream_body(<<>>, Client) ->
+ {ok, Client#client{response_state=waiting}};
+stream_body(Body, #client{req_chunk_size=ChunkSize}=Client)
+ when is_binary(Body) ->
+
+ case Body of
+ _ when byte_size(Body) >= ChunkSize ->
+ << Data:ChunkSize/binary, Rest/binary >> = Body,
+ case send(Client, Data) of
+ ok ->
+ stream_body(Rest, Client);
+ Error ->
+ Error
+ end;
+ _ ->
+ case send(Client, Body) of
+ ok ->
+ {ok, Client#client{response_state=waiting}};
+ Error ->
+ Error
+ end
+ end;
+stream_body(Body, Client) when is_list(Body) ->
+ case send(Client, Body) of
+ ok ->
+ {ok, Client#client{response_state=waiting}};
+ Error ->
+ Error
+ end;
+stream_body({file, FileName}, Client) ->
+ case sendfile(FileName, Client) of
+ {ok, _BytesSent} ->
+ {ok, Client#client{response_state=waiting}};
+ Error ->
+ Error
+ end.
+
send(#client{transport=Transport, socket=Skt}, Data) ->
Transport:send(Skt, Data).
@@ -88,7 +127,6 @@ sendfile(FileName, Client) ->
%% internal
-
handle_body(Headers, Body0) ->
{CLen, CType, Body} = case Body0 of
{form, KVs} ->
@@ -118,33 +156,6 @@ handle_body(Headers, Body0) ->
NewHeaders = hackney_headers:update(Headers, NewHeadersKV),
{NewHeaders, Body}.
-stream_body(<<>>, _Client) ->
- ok;
-stream_body(Body, #client{req_chunk_size=ChunkSize}=Client)
- when is_binary(Body) ->
-
- case Body of
- _ when byte_size(Body) >= ChunkSize ->
- << Data:ChunkSize/binary, Rest/binary >> = Body,
- case send(Client, Data) of
- ok ->
- stream_body(Rest, Client);
- Error ->
- Error
- end;
- _ ->
- send(Client, Body)
- end;
-stream_body(Body, Client) when is_list(Body) ->
- send(Client, Body);
-stream_body({file, FileName}, Client) ->
- case sendfile(FileName, Client) of
- {ok, _BytesSent} ->
- ok;
- Error ->
- Error
- end.
-
sendfile_fallback(Fd, Client) ->
{ok, CurrPos} = file:position(Fd, {cur, 0}),
{ok, _NewPos} = file:position(Fd, {bof, 0}),
View
14 src/hackney_response.erl
@@ -10,15 +10,15 @@
-include("hackney.hrl").
--export([init/1,
+-export([start_response/1,
stream_status/1,
stream_headers/1, stream_header/1,
stream_body/1,
body/1, body/2, skip_body/1,
close/1]).
-%% @doc init response
-init(Client) ->
+%% @doc Start the response It parse the request lines and headers.
+start_response(#client{response_state=waiting} = Client) ->
case stream_status(Client) of
{ok, Status, _Reason, Client1} ->
case stream_headers(Client1) of
@@ -29,9 +29,11 @@ init(Client) ->
end;
Error ->
Error
- end.
-
+ end;
+start_response(_) ->
+ {error, invalide_state}.
+%% @doc parse the status line
stream_status(#client{buffer=Buf}=Client) ->
case binary:split(Buf, <<"\r\n">>) of
[Line, Rest] ->
@@ -55,7 +57,7 @@ parse_status(<< "HTTP/", High, ".", Low, " ", Status/binary >>, Client)
{ok, StatusInt, Reason, Client#client{version=Version,
response_state=on_header}}.
-
+%% @doc fetch all headers
stream_headers(Client) ->
stream_headers(Client, []).
Please sign in to comment.
Something went wrong with that request. Please try again.