aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-09-26 10:43:12 +0200
committerLoïc Hoguin <[email protected]>2019-09-26 10:53:00 +0200
commitd86d55c1f90b37d991e20ad0f1ac37b1e38b36e1 (patch)
tree23a34ec9158d6abfddc6142923535304c02662c1
parentd9a970be90d0105af215531d74809878f9c21338 (diff)
downloadgun-d86d55c1f90b37d991e20ad0f1ac37b1e38b36e1.tar.gz
gun-d86d55c1f90b37d991e20ad0f1ac37b1e38b36e1.tar.bz2
gun-d86d55c1f90b37d991e20ad0f1ac37b1e38b36e1.zip
Add a function to change the connection owner
While at it the gun:info/1 function has been fixed to work even when we are in the not_connected state, and the owner is now also returned.
-rw-r--r--doc/src/manual/gun.asciidoc1
-rw-r--r--doc/src/manual/gun.info.asciidoc2
-rw-r--r--doc/src/manual/gun.set_owner.asciidoc57
-rw-r--r--src/gun.erl39
-rw-r--r--test/gun_SUITE.erl14
5 files changed, 107 insertions, 6 deletions
diff --git a/doc/src/manual/gun.asciidoc b/doc/src/manual/gun.asciidoc
index 478d40b..9670e2e 100644
--- a/doc/src/manual/gun.asciidoc
+++ b/doc/src/manual/gun.asciidoc
@@ -16,6 +16,7 @@ Connection:
* link:man:gun:open(3)[gun:open(3)] - Open a connection to the given host and port
* link:man:gun:open_unix(3)[gun:open_unix(3)] - Open a connection to the given Unix domain socket
+* link:man:gun:set_owner(3)[gun:set_owner(3)] - Set a new owner for the connection
* link:man:gun:shutdown(3)[gun:shutdown(3)] - Gracefully close the connection
* link:man:gun:close(3)[gun:close(3)] - Brutally close the connection
* link:man:gun:info(3)[gun:info(3)] - Obtain information about the connection
diff --git a/doc/src/manual/gun.info.asciidoc b/doc/src/manual/gun.info.asciidoc
index cf861c9..edd97f6 100644
--- a/doc/src/manual/gun.info.asciidoc
+++ b/doc/src/manual/gun.info.asciidoc
@@ -12,6 +12,7 @@ info(ConnPid) -> Info
ConnPid :: pid()
Info :: #{
+ owner => pid(),
socket => inet:socket() | ssl:sslsocket(),
transport => tcp | tls,
protocol => http | http2 | socks | ws,
@@ -45,6 +46,7 @@ the connection.
== Changelog
+* *2.0*: The value `owner` was added.
* *1.3*: The values `socket`, `transport`, `protocol`, `origin_host`,
`origin_port` and `intermediaries` were added.
* *1.0*: Function introduced.
diff --git a/doc/src/manual/gun.set_owner.asciidoc b/doc/src/manual/gun.set_owner.asciidoc
new file mode 100644
index 0000000..ee12a67
--- /dev/null
+++ b/doc/src/manual/gun.set_owner.asciidoc
@@ -0,0 +1,57 @@
+= gun:set_owner(3)
+
+== Name
+
+gun:set_owner - Set a new owner for the connection
+
+== Description
+
+[source,erlang]
+----
+set_owner(ConnPid, OwnerPid) -> ok
+
+ConnPid :: pid()
+OwnerPid :: pid()
+----
+
+Set a new owner for the connection.
+
+Only the current owner of the connection can set a new
+owner.
+
+Gun monitors the owner of the connection and automatically
+shuts down gracefully when the owner exits.
+
+== Arguments
+
+ConnPid::
+
+The pid of the Gun connection process.
+
+OwnerPid::
+
+The pid of the new owner for the connection.
+
+== Return value
+
+The atom `ok` is returned.
+
+== Changelog
+
+* *2.0*: Function introduced.
+
+== Examples
+
+.Set a new owner for the connection
+[source,erlang]
+----
+ok = gun:set_owner(ConnPid, OwnerPid).
+----
+
+== See also
+
+link:man:gun(3)[gun(3)],
+link:man:gun:open(3)[gun:open(3)],
+link:man:gun:open_unix(3)[gun:open_unix(3)],
+link:man:gun:shutdown(3)[gun:shutdown(3)],
+link:man:gun:close(3)[gun:close(3)]
diff --git a/src/gun.erl b/src/gun.erl
index 12f4319..3154b9b 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -23,6 +23,7 @@
-export([open/2]).
-export([open/3]).
-export([open_unix/2]).
+-export([set_owner/2]).
-export([info/1]).
-export([close/1]).
-export([shutdown/1]).
@@ -396,9 +397,14 @@ consider_tracing(ServerPid, #{trace := true}) ->
consider_tracing(_, _) ->
ok.
+-spec set_owner(pid(), pid()) -> ok.
+set_owner(ServerPid, NewOwnerPid) ->
+ gen_statem:cast(ServerPid, {set_owner, self(), NewOwnerPid}).
+
-spec info(pid()) -> map().
info(ServerPid) ->
{_, #state{
+ owner=Owner,
socket=Socket,
transport=Transport,
protocol=Protocol,
@@ -407,22 +413,33 @@ info(ServerPid) ->
origin_port=OriginPort,
intermediaries=Intermediaries
}} = sys:get_state(ServerPid),
- {ok, {SockIP, SockPort}} = Transport:sockname(Socket),
- #{
+ Info0 = #{
+ owner => Owner,
socket => Socket,
transport => case OriginScheme of
<<"http">> -> tcp;
<<"https">> -> tls
end,
- protocol => Protocol:name(),
- sock_ip => SockIP,
- sock_port => SockPort,
origin_scheme => OriginScheme,
origin_host => OriginHost,
origin_port => OriginPort,
%% Intermediaries are listed in the order data goes through them.
intermediaries => lists:reverse(Intermediaries)
- }.
+ },
+ Info = case Socket of
+ undefined ->
+ Info0;
+ _ ->
+ {ok, {SockIP, SockPort}} = Transport:sockname(Socket),
+ Info0#{
+ sock_ip => SockIP,
+ sock_port => SockPort
+ }
+ end,
+ case Protocol of
+ undefined -> Info;
+ _ -> Info#{protocol => Protocol:name()}
+ end.
-spec close(pid()) -> ok.
close(ServerPid) ->
@@ -1253,6 +1270,16 @@ handle_common_connected_no_input(Type, Event, StateName, State) ->
handle_common(Type, Event, StateName, State).
%% Common events.
+handle_common(cast, {set_owner, CurrentOwner, NewOwner}, _,
+ State=#state{owner=CurrentOwner, status={up, CurrentOwnerRef}}) ->
+ demonitor(CurrentOwnerRef, [flush]),
+ NewOwnerRef = monitor(process, NewOwner),
+ {keep_state, State#state{owner=NewOwner, status={up, NewOwnerRef}}};
+%% We cannot change the owner when we are shutting down.
+handle_common(cast, {set_owner, CurrentOwner, _}, _, #state{owner=CurrentOwner}) ->
+ CurrentOwner ! {gun_error, self(), {badstate,
+ "The owner of the connection cannot be changed when the connection is shutting down."}},
+ keep_state_and_state;
handle_common(cast, {shutdown, Owner}, StateName, State=#state{
owner=Owner, status=Status, socket=Socket, transport=Transport, protocol=Protocol}) ->
case {Socket, Protocol} of
diff --git a/test/gun_SUITE.erl b/test/gun_SUITE.erl
index a0ecddd..715bec5 100644
--- a/test/gun_SUITE.erl
+++ b/test/gun_SUITE.erl
@@ -384,6 +384,20 @@ retry_timeout(_) ->
error(shutdown_too_late)
end.
+set_owner(_) ->
+ doc("The owner of the connection can be changed."),
+ Self = self(),
+ Pid = spawn(fun() ->
+ {ok, ConnPid} = gun:open("localhost", 12345),
+ gun:set_owner(ConnPid, Self),
+ Self ! {conn, ConnPid}
+ end),
+ Ref = monitor(process, Pid),
+ receive {'DOWN', Ref, process, Pid, _} -> ok after 1000 -> error(timeout) end,
+ ConnPid = receive {conn, C} -> C after 1000 -> error(timeout) end,
+ #{owner := Self} = gun:info(ConnPid),
+ gun:close(ConnPid).
+
shutdown_reason(_) ->
doc("The last connection failure must be propagated."),
{ok, Pid} = gun:open("localhost", 12345, #{retry => 0}),