File shotgun-0.3.0-git.patch of Package shotgun
diff --git a/README.md b/README.md
index 1c09c2e..7123c03 100644
--- a/README.md
+++ b/README.md
@@ -122,14 +122,14 @@ entries:
response. **It currently only works for GET requests.**. Default value is
`false`.
-- `async_data :: binary | sse`: when `async` is `true` the mode specifies
-how the data received will be processed. `binary` mode treats eat chunk received
+- `async_mode :: binary | sse`: when `async` is `true` the mode specifies
+how the data received will be processed. `binary` mode treats each chunk received
as raw binary. `sse` mode buffers each chunk, splitting the data received into
SSE. Default value is `binary`.
- `handle_event :: fun((fin | nofin, reference(), binary()) -> any())`: this function
-will be called each time either a chunk is received (`async_data` = `binary`) or
-an event is parsed (`async_data` = `sse`). If no handle_event function is
+will be called each time either a chunk is received (`async_mode` = `binary`) or
+an event is parsed (`async_mode` = `sse`). If no handle_event function is
provided the data received is added to a queue, whose values can be obtained
calling the `shotgun:events/1`. Default value is `undefined`.
@@ -181,9 +181,6 @@ rebar3 shell
```
## Contact Us
-For **questions** or **general comments** regarding the use of this library,
-please use our public [hipchat room](http://inaka.net/hipchat).
-
If you find any **bugs** or have a **problem** while using this library, please
[open an issue](https://github.com/inaka/shotgun/issues/new) in this repo
(or a pull request :)).
diff --git a/rebar.config b/rebar.config
index b7cbb8b..1567ee0 100644
--- a/rebar.config
+++ b/rebar.config
@@ -24,8 +24,8 @@
{test, [
{deps, [ {katana_test, {git, "https://github.com/inaka/katana-test.git", {tag, "0.0.6"}}}
, {mixer, {git, "https://github.com/inaka/mixer.git", {tag, "0.1.5"}}}
- , {cowboy, {git, "https://github.com/ninenines/cowboy.git", {tag, "2.0.0-pre.3"}}}
- , {lasse, {git, "https://github.com/inaka/lasse.git", {tag, "cowboy2"}}}
+ , {cowboy, {git, "https://github.com/ninenines/cowboy.git", {tag, "2.4.0"}}}
+ , {lasse, {git, "https://github.com/inaka/lasse.git", {tag, "1.2.0"}}}
]}
]},
{shell, [
@@ -63,7 +63,7 @@
%% == Dependencies ==
-{deps, [{gun, "1.0.0-pre.1"}]}.
+{deps, [{gun, {git, "https://github.com/ninenines/gun", {tag, "1.3.0"}}}]}.
%% == Dialyzer ==
@@ -81,4 +81,4 @@
%% == Shell ==
-{shell, [{apps, [sync]}]}.
\ No newline at end of file
+{shell, [{apps, [sync]}]}.
diff --git a/rebar.config.script b/rebar.config.script
new file mode 100644
index 0000000..1317629
--- /dev/null
+++ b/rebar.config.script
@@ -0,0 +1,8 @@
+case erlang:function_exported(rebar3, main, 1) of
+ true -> % rebar3
+ CONFIG;
+ false -> % rebar 2.x
+ [{deps, [
+ {gun, ".*", {git, "https://github.com/ninenines/gun.git", "1.3.0"}}
+ ]} | lists:keydelete(deps, 1, CONFIG)]
+end.
diff --git a/rebar.lock b/rebar.lock
index 7379111..4dfb799 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,3 +1,8 @@
-[{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.3.0">>},1},
- {<<"gun">>,{pkg,<<"gun">>,<<"1.0.0-pre.1">>},0},
- {<<"ranch">>,{pkg,<<"ranch">>,<<"1.1.0">>},1}].
+[{<<"cowlib">>,
+ {git,"https://github.com/ninenines/cowlib",
+ {ref,"106ba84bb04537879d8ce59321a04e0682110b91"}},
+ 1},
+ {<<"gun">>,
+ {git,"https://github.com/ninenines/gun",
+ {ref,"e7dd9f227e46979d8073e71c683395a809b78cb4"}},
+ 0}].
diff --git a/src/shotgun.erl b/src/shotgun.erl
index c7b3e93..25a8316 100644
--- a/src/shotgun.erl
+++ b/src/shotgun.erl
@@ -79,12 +79,14 @@
%% timeout is passed to gun:await_up. Default if not specified
%% is 5000 ms.
, timeout => timeout()
+ %% gun_opts are passed to gun:open
+ , gun_opts => gun:opts()
}.
-type connection() :: pid().
-type http_verb() :: get | post | head | delete | patch | put | options.
-type uri() :: iodata().
--type headers() :: #{} | proplists:proplist().
+-type headers() :: #{_ => _} | proplists:proplist().
-type body() :: iodata() | body_chunked.
-type options() ::
#{ async => boolean()
@@ -124,7 +126,7 @@ stop() ->
-spec start_link(string(), integer(), connection_type(), open_opts()) ->
{ok, pid()} | ignore | {error, term()}.
start_link(Host, Port, Type, Opts) ->
- gen_fsm:start_link(shotgun, [Host, Port, Type, Opts], []).
+ gen_fsm:start_link(shotgun, [{Host, Port, Type, Opts}], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% API
@@ -152,7 +154,9 @@ open(Host, Port, Opts) when is_map(Opts) ->
%% transport options.
-spec open(Host :: string(), Port :: integer(), Type :: connection_type(),
Opts :: open_opts()) ->
- {ok, pid()} | {error, gun_open_failed, gun_open_timeout}.
+ {ok, pid()} | {ok, pid(), term()} |
+ {error, already_present | {already_started, pid()} |
+ gun_open_failed | gun_open_timeout}.
open(Host, Port, Type, Opts) ->
supervisor:start_child(shotgun_sup, [Host, Port, Type, Opts]).
@@ -333,24 +337,39 @@ parse_event(EventBin) ->
%% gen_fsm callbacks
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--type state() :: #{}.
+-type state() :: #{ async => boolean()
+ , async_mode => false | binary | sse
+ , buffer => binary()
+ , data => binary()
+ , from => term()
+ , handle_event => term()
+ , headers => term()
+ , pending_requests => queue:queue() | undefined
+ , pid => pid() | undefined
+ , responses => queue:queue() | undefined
+ , status_code => integer() | undefined
+ , stream => reference() | undefined}.
%% @private
--spec init([term()]) ->
+-spec init([{string(), integer(), connection_type(), open_opts()}]) ->
{ok, at_rest, state()} | {stop, gun_open_timeout} | {stop, gun_open_failed}.
-init([Host, Port, Type, Opts]) ->
+init([{Host, Port, Type, Opts}]) ->
GunType = case Type of
http -> tcp;
https -> ssl
end,
TransportOpts = maps:get(transport_opts, Opts, []),
- GunOpts = #{transport => GunType,
+ PassedGunOpts = maps:get(gun_opts, Opts, #{}),
+ DefaultGunOpts = #{
+ transport => GunType,
retry => 1,
retry_timeout => 1,
transport_opts => TransportOpts
},
+ GunOpts = maps:merge(DefaultGunOpts, PassedGunOpts),
Timeout = maps:get(timeout, Opts, 5000),
{ok, Pid} = gun:open(Host, Port, GunOpts),
+ _ = monitor(process, Pid),
case gun:await_up(Pid, Timeout) of
{ok, _} ->
State = clean_state(),
@@ -362,6 +381,11 @@ init([Host, Port, Type, Opts]) ->
%gun currently terminates with reason normal if gun:open fails to open
%the requested connection. This bubbles up through gun:await_up.
{error, normal} ->
+ {stop, gun_open_failed};
+ %gun can terminate with reason {shutdown, nxdomain}; however, that's not
+ %explicitly specced and makes dialyzer unhappy, so we loosely pattern
+ %match it here.
+ {error, _} ->
{stop, gun_open_failed}
end.
@@ -416,7 +440,9 @@ terminate(_Reason, _StateName, #{pid := Pid} = _State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @private
--spec at_rest(term(), pid(), term()) -> term().
+-spec at_rest(term(), pid(), state()) ->
+ {stop, {error, any()}, atom(), state()} |
+ {next_state, atom(), state(), timeout()}.
at_rest(Event, From, State) ->
enqueue_work_or_stop(at_rest, Event, From, State).
@@ -431,12 +457,16 @@ wait_response(Event, From, State) ->
enqueue_work_or_stop(wait_response, Event, From, State).
%% @private
--spec receive_data(term(), pid(), term()) -> term().
+-spec receive_data(term(), pid(), term()) ->
+ {stop, {error, any()}, atom(), state()} |
+ {next_state, atom(), state(), timeout()}.
receive_data(Event, From, State) ->
enqueue_work_or_stop(receive_data, Event, From, State).
%% @private
--spec receive_chunk(term(), pid(), term()) -> term().
+-spec receive_chunk(term(), pid(), term()) ->
+ {stop, {error, any()}, atom(), state()} |
+ {next_state, atom(), state(), timeout()}.
receive_chunk(Event, From, State) ->
enqueue_work_or_stop(receive_chunk, Event, From, State).
@@ -444,6 +474,8 @@ receive_chunk(Event, From, State) ->
%If we don't, stay in at_rest.
%% @private
-spec at_rest(any(), state()) -> {next_state, atom(), state()}.
+at_rest({'DOWN', _, _, _, Reason}, _State) ->
+ exit(Reason);
at_rest(timeout, State) ->
case get_work(State) of
no_work ->
@@ -480,7 +512,9 @@ at_rest({HttpVerb, {_, _, Body} = Args, From}, State = #{pid := Pid}) ->
{next_state, wait_response, NewState}.
%% @private
--spec wait_response(term(), term()) -> term().
+-spec wait_response(term(), term()) ->
+ {stop, {error, any()}, atom(), state()} |
+ {next_state, atom(), state(), timeout()}.
wait_response({'DOWN', _, _, _, Reason}, _State) ->
exit(Reason);
wait_response({gun_response, _Pid, _StreamRef, fin, StatusCode, Headers},
@@ -516,7 +550,9 @@ wait_response(Event, State) ->
%% @private
%% @doc Regular response
--spec receive_data(term(), term()) -> term().
+-spec receive_data(term(), term()) ->
+ {stop, {error, any()}, atom(), state()} |
+ {next_state, atom(), state(), timeout()}.
receive_data({'DOWN', _, _, _, _Reason}, _State) ->
error(incomplete);
receive_data({gun_data, _Pid, StreamRef, nofin, Data},
@@ -539,7 +575,7 @@ receive_data({gun_error, _Pid, StreamRef, _Reason},
%% @private
%% @doc Chunked data response
--spec receive_chunk(term(), term()) -> term().
+-spec receive_chunk(term(), state()) -> term().
receive_chunk({'DOWN', _, _, _, _Reason}, _State) ->
error(incomplete);
receive_chunk({gun_data, _Pid, StreamRef, IsFin, Data}, State) ->
@@ -558,12 +594,12 @@ receive_chunk({gun_error, _Pid, _StreamRef, _Reason}, State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @private
--spec clean_state() -> map().
+-spec clean_state() -> state().
clean_state() ->
clean_state(#{}).
%% @private
--spec clean_state(map()) -> map(); (queue:queue()) -> map().
+-spec clean_state(map()) -> state().
clean_state(State) ->
Responses = maps:get(responses, State, queue:new()),
Requests = maps:get(pending_requests, State, queue:new()),
@@ -662,7 +698,9 @@ basic_auth_header(Headers) ->
end.
%% @private
--spec encode_basic_auth(string(), string()) -> binary().
+-type ascii_string() :: [1..255].
+
+-spec encode_basic_auth(ascii_string(), ascii_string()) -> binary().
encode_basic_auth(Username, Password) ->
base64:encode(Username ++ [$: | Password]).
diff --git a/test/http_server/http_binary_handler.erl b/test/http_server/http_binary_handler.erl
index 1067666..a56b5ab 100644
--- a/test/http_server/http_binary_handler.erl
+++ b/test/http_server/http_binary_handler.erl
@@ -10,9 +10,11 @@
init(Req, _Opts) ->
case cowboy_req:method(Req) of
<<"GET">> ->
- Headers = [{<<"content-type">>, <<"text/event-stream">>},
- {<<"cache-control">>, <<"no-cache">>}],
- Req1 = cowboy_req:chunked_reply(200, Headers, Req),
+ Headers = #{
+ <<"content-type">> => <<"text/event-stream">>,
+ <<"cache-control">> => <<"no-cache">>
+ },
+ Req1 = cowboy_req:stream_reply(200, Headers, Req),
shotgun_test_utils:auto_send(count),
{cowboy_loop, Req1, 1};
_OtherMethod ->
@@ -29,7 +31,7 @@ info(count, Req, Count) ->
true ->
{stop, Req, 0};
false ->
- ok = cowboy_req:chunk(integer_to_binary(Count), Req),
+ ok = cowboy_req:stream_body(integer_to_binary(Count), nofin, Req),
shotgun_test_utils:auto_send(count),
{ok, Req, Count + 1}
end.
diff --git a/test/http_server/http_server.erl b/test/http_server/http_server.erl
index a2f5aca..80ecb26 100644
--- a/test/http_server/http_server.erl
+++ b/test/http_server/http_server.erl
@@ -41,23 +41,14 @@ start_phase(start_cowboy_http, _StartType, []) ->
[{ '_'
, [ {"/", http_simple_handler, []}
, {"/basic-auth", http_basic_auth_handler, []}
-
, {"/chunked-sse[/:count]", lasse_handler, [http_sse_handler]}
, {"/chunked-binary", http_binary_handler, []}
]
}
],
Dispatch = cowboy_router:compile(Routes),
- RanchOptions = [{port, Port}],
- CowboyOptions =
- [
- {env,
- [
- {dispatch, Dispatch}
- ]},
- {compress, true},
- {timeout, 12000}
- ],
+ TransportOptions = [{port, Port}],
+ ProtocolOptions = #{env => #{dispatch => Dispatch}},
{ok, _} =
- cowboy:start_http(http_server, ListenerCount, RanchOptions, CowboyOptions),
+ cowboy:start_clear(http_server, TransportOptions, ProtocolOptions),
ok.
diff --git a/test/http_server/http_simple_handler.erl b/test/http_server/http_simple_handler.erl
index ef1f0f3..d96895a 100644
--- a/test/http_server/http_simple_handler.erl
+++ b/test/http_server/http_simple_handler.erl
@@ -42,7 +42,7 @@ handle_get(Req, State) ->
{Body, Req, State}.
handle_post(Req, State) ->
- {ok, Data, Req1} = cowboy_req:body(Req),
+ {ok, Data, Req1} = cowboy_req:read_body(Req),
Method = cowboy_req:method(Req1),
Body = [Method, <<": ">>, Data],
Req2 = cowboy_req:set_resp_body(Body, Req1),
diff --git a/test/shotgun_meta_SUITE.erl b/test/shotgun_meta_SUITE.erl
index 87dcce5..6e73d18 100644
--- a/test/shotgun_meta_SUITE.erl
+++ b/test/shotgun_meta_SUITE.erl
@@ -4,5 +4,9 @@
-mixin([ktn_meta_SUITE]).
-export([init_per_suite/1]).
+-export([end_per_suite/1]).
init_per_suite(Config) -> [{application, shotgun} | Config].
+
+end_per_suite(Config) ->
+ Config.