aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gun.erl80
-rw-r--r--src/gun_default_event_h.erl29
-rw-r--r--src/gun_event.erl72
3 files changed, 159 insertions, 22 deletions
diff --git a/src/gun.erl b/src/gun.erl
index 7376a33..e958d2e 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -106,6 +106,7 @@
-type opts() :: #{
connect_timeout => timeout(),
+ event_handler => {module(), any()},
http_opts => http_opts(),
http2_opts => http2_opts(),
protocols => [http | http2],
@@ -184,7 +185,9 @@
transport :: module(),
messages :: {atom(), atom(), atom()},
protocol :: module(),
- protocol_state :: any()
+ protocol_state :: any(),
+ event_handler :: module(),
+ event_handler_state :: any()
}).
%% Connection.
@@ -233,6 +236,8 @@ check_options([{connect_timeout, infinity}|Opts]) ->
check_options(Opts);
check_options([{connect_timeout, T}|Opts]) when is_integer(T), T >= 0 ->
check_options(Opts);
+check_options([{event_handler, {Mod, _}}|Opts]) when is_atom(Mod) ->
+ check_options(Opts);
check_options([{http_opts, ProtoOpts}|Opts]) when is_map(ProtoOpts) ->
case gun_http:check_options(ProtoOpts) of
ok ->
@@ -724,18 +729,29 @@ init({Owner, Host, Port, Opts}) ->
tls -> {<<"https">>, gun_tls}
end,
OwnerRef = monitor(process, Owner),
+ {EventHandler, EventHandlerState0} = maps:get(event_handler, Opts,
+ {gun_default_event_h, undefined}),
+ EventHandlerState = EventHandler:init(#{
+ owner => Owner,
+ transport => OriginTransport,
+ origin_scheme => OriginScheme,
+ origin_host => Host,
+ origin_port => Port,
+ opts => Opts
+ }, EventHandlerState0),
State = #state{owner=Owner, owner_ref=OwnerRef,
host=Host, port=Port, origin_scheme=OriginScheme,
origin_host=Host, origin_port=Port, opts=Opts,
- transport=Transport, messages=Transport:messages()},
+ transport=Transport, messages=Transport:messages(),
+ event_handler=EventHandler, event_handler_state=EventHandlerState},
{ok, not_connected, State,
{next_event, internal, {retries, Retry}}}.
default_transport(443) -> tls;
default_transport(_) -> tcp.
-not_connected(_, {retries, Retries},
- State=#state{host=Host, port=Port, opts=Opts, transport=Transport}) ->
+not_connected(_, {retries, Retries}, State0=#state{host=Host, port=Port, opts=Opts,
+ transport=Transport, event_handler=EventHandler, event_handler_state=EventHandlerState0}) ->
TransOpts0 = maps:get(transport_opts, Opts, []),
TransOpts1 = case Transport of
gun_tcp -> TransOpts0;
@@ -743,27 +759,47 @@ not_connected(_, {retries, Retries},
end,
TransOpts = [binary, {active, false}|TransOpts1],
ConnectTimeout = maps:get(connect_timeout, Opts, infinity),
+ ConnectEvent = #{
+ host => Host,
+ port => Port,
+ transport => Transport:name(),
+ transport_opts => TransOpts,
+ timeout => ConnectTimeout
+ },
+ EventHandlerState1 = EventHandler:connect_start(ConnectEvent, EventHandlerState0),
case Transport:connect(Host, Port, TransOpts, ConnectTimeout) of
- {ok, Socket} when Transport =:= gun_tcp ->
- Protocol = case maps:get(protocols, Opts, [http]) of
- [http] -> gun_http;
- [http2] -> gun_http2
+ {ok, Socket} ->
+ Protocol = case Transport of
+ gun_tcp ->
+ case maps:get(protocols, Opts, [http]) of
+ [http] -> gun_http;
+ [http2] -> gun_http2
+ end;
+ gun_tls ->
+ case ssl:negotiated_protocol(Socket) of
+ {ok, <<"h2">>} -> gun_http2;
+ _ -> gun_http
+ end
end,
- {next_state, connected, State,
+ EventHandlerState = EventHandler:connect_end(ConnectEvent#{
+ socket => Socket,
+ protocol => Protocol:name()
+ }, EventHandlerState1),
+ {next_state, connected, State0#state{event_handler_state=EventHandlerState},
{next_event, internal, {connected, Socket, Protocol}}};
- {ok, Socket} when Transport =:= gun_tls ->
- Protocol = case ssl:negotiated_protocol(Socket) of
- {ok, <<"h2">>} -> gun_http2;
- _ -> gun_http
- end,
- {next_state, connected, State,
- {next_event, internal, {connected, Socket, Protocol}}};
- {error, Reason} when Retries =:= 0 ->
- {stop, {shutdown, Reason}};
- {error, _Reason} ->
- Timeout = maps:get(retry_timeout, Opts, 5000),
- {keep_state, State,
- {state_timeout, Timeout, {retries, Retries - 1}}}
+ {error, Reason} ->
+ EventHandlerState = EventHandler:connect_end(ConnectEvent#{
+ error => Reason
+ }, EventHandlerState1),
+ State = State0#state{event_handler_state=EventHandlerState},
+ case Retries of
+ 0 ->
+ {stop, {shutdown, Reason}, State};
+ _ ->
+ Timeout = maps:get(retry_timeout, Opts, 5000),
+ {keep_state, State,
+ {state_timeout, Timeout, {retries, Retries - 1}}}
+ end
end;
not_connected({call, From}, {stream_info, _}, _) ->
{keep_state_and_data, {reply, From, {error, not_connected}}};
diff --git a/src/gun_default_event_h.erl b/src/gun_default_event_h.erl
new file mode 100644
index 0000000..e242e84
--- /dev/null
+++ b/src/gun_default_event_h.erl
@@ -0,0 +1,29 @@
+%% Copyright (c) 2019, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(gun_default_event_h).
+-behavior(gun_event).
+
+-export([init/2]).
+-export([connect_start/2]).
+-export([connect_end/2]).
+
+init(_EventData, State) ->
+ State.
+
+connect_start(_EventData, State) ->
+ State.
+
+connect_end(_EventData, State) ->
+ State.
diff --git a/src/gun_event.erl b/src/gun_event.erl
new file mode 100644
index 0000000..fffcafe
--- /dev/null
+++ b/src/gun_event.erl
@@ -0,0 +1,72 @@
+%% Copyright (c) 2019, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(gun_event).
+
+%% init.
+
+-type init_event() :: #{
+ owner := pid(),
+ transport := tcp | tls,
+ origin_scheme := binary(),
+ origin_host := inet:hostname() | inet:ip_address(),
+ origin_port := inet:port_number(),
+ opts := gun:opts()
+}.
+
+-callback init(init_event(), State) -> State.
+
+%% connect_start/connect_end.
+
+-type connect_event() :: #{
+ host := inet:hostname() | inet:ip_address(),
+ port := inet:port_number(),
+ transport := tcp | tls,
+ transport_opts := [gen_tcp:connect_option()] | [ssl:connect_option()],
+ timeout := timeout(),
+ socket => inet:socket() | ssl:sslsocket() | pid(),
+ protocol => http | http2,
+ error => any()
+}.
+
+-callback connect_start(connect_event(), State) -> State.
+-callback connect_end(connect_event(), State) -> State.
+
+%% @todo domain_lookup_start
+%% @todo domain_lookup_end
+%% @todo tls_handshake_start
+%% @todo tls_handshake_end
+%% @todo origin_changed
+%% @todo transport_changed
+%% @todo protocol_changed
+%% @todo disconnected
+%% @todo terminate
+%% @todo stream_start
+%% @todo stream_end
+%% @todo request_start
+%% @todo request_headers
+%% @todo request_end
+%% @todo response_start (call it once per inform + one for the response)
+%% @todo response_inform
+%% @todo response_headers
+%% @todo response_end
+%% @todo push_promise_start
+%% @todo push_promise_end
+%% @todo ws_upgrade_start
+%% @todo ws_upgrade_end
+%% @todo ws_frame_read_start
+%% @todo ws_frame_read_header
+%% @todo ws_frame_read_end
+%% @todo ws_frame_write_start
+%% @todo ws_frame_write_end