From f879b2782647317c5910eb763607a8c93a1f0690 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Wed, 6 May 2020 13:54:47 +0200 Subject: [PATCH 1/4] Pass on supported gun opts in connect() --- src/apns_connection.erl | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/apns_connection.erl b/src/apns_connection.erl index 2a7767b..a98b82e 100644 --- a/src/apns_connection.erl +++ b/src/apns_connection.erl @@ -68,6 +68,7 @@ -type path() :: string(). -type notification() :: binary(). -type type() :: certdata | cert | token. +-type http_opts() :: #{ keepalive => non_neg_integer() }. -type keydata() :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo' , binary()}. @@ -87,6 +88,7 @@ , timeout => integer() , type := type() , proxy_info => proxy_info() + , http_opts => http_opts() }. -type state() :: #{ connection := connection() @@ -227,25 +229,30 @@ open_origin(internal, _, #{connection := Connection} = StateData) -> Host = host(Connection), Port = port(Connection), TransportOpts = transport_opts(Connection), + Opts0 = #{ protocols => [http2] + , transport_opts => TransportOpts + , retry => 0 + }, + Opts = add_gun_opts(Connection, Opts0), {next_state, open_common, StateData, {next_event, internal, { Host , Port - , #{ protocols => [http2] - , transport_opts => TransportOpts - , retry => 0 - }}}}. + , Opts}}}. -spec open_proxy(_, _, _) -> _. open_proxy(internal, _, StateData) -> #{connection := Connection} = StateData, #{type := connect, host := ProxyHost, port := ProxyPort} = proxy(Connection), + Opts0 = #{ protocols => [http] + , transport => tcp + , retry => 0 + }, + Opts = add_gun_opts(Connection, Opts0), {next_state, open_common, StateData, {next_event, internal, { ProxyHost , ProxyPort - , #{ protocols => [http] - , transport => tcp - , retry => 0 - }}}}. + , Opts + }}}. %% This function exists only to make Elvis happy. %% I do not think it makes things any easier to read. @@ -492,3 +499,14 @@ backoff(N, Ceiling) -> NString = float_to_list(NextN, [{decimals, 0}]), list_to_integer(NString) end. + +add_gun_opts(Connection, Opts) -> + maps:merge(maps:with(gun_opts_keys(), Connection), Opts). + +gun_opts_keys() -> + [ connect_timeout + , http_opts + , http2_opts + , retry_timeout + , trace + , transport ]. From f9f8118c9934eae9aad6a1c2dbd64df0a807d678 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Wed, 6 May 2020 15:01:12 +0200 Subject: [PATCH 2/4] Increase elvis DRY complexity --- elvis.config | 1 + 1 file changed, 1 insertion(+) diff --git a/elvis.config b/elvis.config index d122bc6..12a3675 100644 --- a/elvis.config +++ b/elvis.config @@ -9,6 +9,7 @@ rules => [ {elvis_style, line_length, #{limit => 100}} , {elvis_style, god_modules, #{limit => 25, ignore => [apns_connection]}} + , {elvis_style, dont_repeat_yourself, #{min_complexity => 12}} ] }, #{dirs => ["."], From 21a0dc2a5507694251f856883c2b72fedfa71908 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Wed, 6 May 2020 16:13:33 +0200 Subject: [PATCH 3/4] Add CT test case for gun params passing --- test/connection_SUITE.erl | 44 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/test/connection_SUITE.erl b/test/connection_SUITE.erl index 40be5a8..5d70518 100644 --- a/test/connection_SUITE.erl +++ b/test/connection_SUITE.erl @@ -10,6 +10,7 @@ , certdata_keydata_connection/1 , connect/1 , connect_without_name/1 + , connect_with_gun_params/1 , gun_connection_lost/1 , gun_connection_lost_timeout/1 , gun_connection_killed/1 @@ -32,6 +33,7 @@ all() -> [ default_connection , certdata_keydata_connection , connect , connect_without_name + , connect_with_gun_params , gun_connection_lost , gun_connection_lost_timeout , gun_connection_killed @@ -122,6 +124,27 @@ connect_without_name(_Config) -> [_] = meck:unload(), ok. +-spec connect_with_gun_params(config()) -> ok. +connect_with_gun_params(_Config) -> + connect_with_gun_param(connect_timeout, 10000), + connect_with_gun_param(http_opts, #{keepalive => 10000}), + connect_with_gun_param(http2_opts, #{keepalive => 10000}), + connect_with_gun_param(retry_timeout, 10000), + connect_with_gun_param(trace, true), + connect_with_gun_param(transport, tls). + +-spec connect_with_gun_param(atom(), any()) -> ok. +connect_with_gun_param(K, V) -> + ConnectionName = ?FUNCTION_NAME, + Connection = apns_connection:default_connection(cert, ConnectionName), + ok = mock_gun_open_param(K), + {ok, ServerPid} = apns:connect(Connection#{K => V}), + true = is_process_alive(ServerPid), + ok = close_connection(ServerPid), + [_] = meck:unload(), + ct:log("Gun param ~p => ~p passed correctly", [K, V]), + ok. + -spec gun_connection_lost(config()) -> ok. gun_connection_lost(_Config) -> ok = mock_gun_open(), @@ -394,10 +417,27 @@ test_function() -> -spec mock_gun_open() -> ok. mock_gun_open() -> meck:expect(gun, open, fun(_, _, _) -> + mimick_gun_open() + end). + +-spec mimick_gun_open() -> {ok, pid()}. +mimick_gun_open() -> GunPid = spawn(fun test_function/0), self() ! {gun_up, GunPid, http2}, - {ok, GunPid} - end). + {ok, GunPid}. + +-spec mock_gun_open_param(atom()) -> ok. +mock_gun_open_param(Key) -> + meck:expect( + gun, open, + fun(_, _, Opts) -> + case maps:is_key(Key, Opts) of + true -> + mimick_gun_open(); + false -> + error({missing_key, Key}) + end + end). -spec mock_gun_post() -> ok. mock_gun_post() -> From 6724edcf073f512a01a0b7652223d173db2f2fe0 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Wed, 6 May 2020 16:40:37 +0200 Subject: [PATCH 4/4] move gun-specific opts into sub-structure, add README text --- README.md | 8 ++++++++ src/apns_connection.erl | 12 +++--------- test/connection_SUITE.erl | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 038cfa1..92ee7ca 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ The other way is send all that info as a parameter to `apns:connect/1` function , keyfile => path() , timeout => integer() , type := type() + , gun => gun:opts() }. ``` @@ -137,6 +138,13 @@ certfile => "priv/cert2.pem", keyfile => "priv/key2-noenc.pem", type => cert}). {ok,<0.132.0>} ``` +## Passing options specific to Gun + +The actual connection is handled by the [gun](https://github.com/ninenines/gun) library, +which supports some options of its own. You can pass options specific to `gun` in +the `connection` struct using `gun => GunOpts`. See the `gun` documentation for +examples. + ## Push Notifications over `Provider Certificate` connections In order to send Notifications over `Provider Certificate` connection we will use `apns:push_notification/3,4`. diff --git a/src/apns_connection.erl b/src/apns_connection.erl index a98b82e..24a428d 100644 --- a/src/apns_connection.erl +++ b/src/apns_connection.erl @@ -89,6 +89,7 @@ , type := type() , proxy_info => proxy_info() , http_opts => http_opts() + , gun => gun:opts() }. -type state() :: #{ connection := connection() @@ -501,12 +502,5 @@ backoff(N, Ceiling) -> end. add_gun_opts(Connection, Opts) -> - maps:merge(maps:with(gun_opts_keys(), Connection), Opts). - -gun_opts_keys() -> - [ connect_timeout - , http_opts - , http2_opts - , retry_timeout - , trace - , transport ]. + GunOpts = maps:get(gun, Connection, #{}), + maps:merge(GunOpts, Opts). diff --git a/test/connection_SUITE.erl b/test/connection_SUITE.erl index 5d70518..81e51a5 100644 --- a/test/connection_SUITE.erl +++ b/test/connection_SUITE.erl @@ -138,7 +138,7 @@ connect_with_gun_param(K, V) -> ConnectionName = ?FUNCTION_NAME, Connection = apns_connection:default_connection(cert, ConnectionName), ok = mock_gun_open_param(K), - {ok, ServerPid} = apns:connect(Connection#{K => V}), + {ok, ServerPid} = apns:connect(Connection#{gun => #{K => V}}), true = is_process_alive(ServerPid), ok = close_connection(ServerPid), [_] = meck:unload(),