@@ -34,7 +34,8 @@ all() ->
3434
3535init_per_testcase (TestCase , Config )
3636 when TestCase =:= bad_client_version_test ;
37- TestCase =:= bad_server_version_test ->
37+ TestCase =:= bad_server_version_test ;
38+ TestCase =:= exponential_backoff_test ->
3839 Config ;
3940init_per_testcase (TestCase , Config ) ->
4041 CertDir = cert_dir (),
@@ -51,7 +52,8 @@ init_per_testcase(TestCase, Config) ->
5152
5253end_per_testcase (TestCase , Config )
5354 when TestCase =:= bad_client_version_test ;
54- TestCase =:= bad_server_version_test ->
55+ TestCase =:= bad_server_version_test ;
56+ TestCase =:= exponential_backoff_test ->
5557 Config ;
5658end_per_testcase (_ , Config ) ->
5759 ok = application :stop (grisp_connect ),
@@ -62,6 +64,140 @@ end_per_testcase(_, Config) ->
6264
6365% --- Tests ---------------------------------------------------------------------
6466
67+ exponential_backoff_test (_ ) ->
68+ CertDir = cert_dir (),
69+ CallRef = make_ref (),
70+ Self = self (),
71+
72+ % First we test the exponential backoff algorithm when failing to connect
73+
74+ Apps = grisp_connect_test_server :start (#{
75+ cert_dir => CertDir ,
76+ init_callback => fun (Req , Opts ) ->
77+ Self ! {CallRef , init , os :timestamp ()},
78+ {ok , cowboy_req :reply (400 , #{}, <<" Canceled" >>, Req ), Opts }
79+ end }),
80+
81+ {ok , _ } = application :ensure_all_started (grisp_emulation ),
82+ application :load (grisp_connect ),
83+ {ok , OldConectEnv } = application :get_env (grisp_connect , connect ),
84+ application :set_env (grisp_connect , connect , false ),
85+ {ok , _ } = application :ensure_all_started (grisp_connect ),
86+
87+ {T1 , T2 , T3 , T4 } = try
88+ T1a = os :timestamp (),
89+ grisp_connect :connect (),
90+ T2a = receive
91+ {CallRef , init , V2 } -> V2
92+ after 300 ->
93+ erlang :error (timeout2 )
94+ end ,
95+ T3a = receive
96+ {CallRef , init , V3 } -> V3
97+ after 2300 ->
98+ erlang :error (timeout3 )
99+ end ,
100+ T4a = receive
101+ {CallRef , init , V4 } -> V4
102+ after 4300 ->
103+ erlang :error (timeout4 )
104+ end ,
105+ {T1a , T2a , T3a , T4a }
106+ catch
107+ C1 :R1 :S1 ->
108+ ok = application :stop (grisp_connect ),
109+ application :set_env (grisp_connect , connect , OldConectEnv ),
110+ erlang :raise (C1 , R1 , S1 )
111+ after
112+ grisp_connect_test_server :wait_disconnection (),
113+ ? assertEqual ([], flush ()),
114+ grisp_connect_test_server :stop (Apps )
115+ end ,
116+
117+ % Then we allow the connection to succeed
118+
119+ grisp_connect_test_server :start (#{
120+ cert_dir => CertDir ,
121+ init_callback => fun (Req , Opts ) ->
122+ Self ! {CallRef , init , os :timestamp ()},
123+ Req2 = cowboy_req :set_resp_header (<<" sec-websocket-protocol" >>, <<" grisp-io-v1" >>, Req ),
124+ {cowboy_websocket , Req2 , Opts }
125+ end }),
126+ try
127+ T5 = receive
128+ {CallRef , init , V5 } -> V5
129+ after 8300 ->
130+ erlang :error (timeout5 )
131+ end ,
132+
133+ ? assertMatch (ok , wait_connection ()),
134+ D1 = timer :now_diff (T2 , T1 ) div 1000 ,
135+ D2 = timer :now_diff (T3 , T2 ) div 1000 ,
136+ D3 = timer :now_diff (T4 , T3 ) div 1000 ,
137+ D4 = timer :now_diff (T5 , T4 ) div 1000 ,
138+ ? assert (D1 < 300 , D1 ),
139+ ? assert (D2 > 900 , D2 ), % 100 ms less for reliability
140+ ? assert (D2 < 100 + 1000 * 1 bsl 1 , D2 ), % Extra 100 ms for reliability
141+ ? assert (D3 > 900 , D3 ), % 100 ms less for reliability
142+ ? assert (D3 < 100 + 1000 * 1 bsl 2 , D3 ), % Extra 100 ms for reliability
143+ ? assert (D4 > 900 , D4 ), % 100 ms less for reliability
144+ ? assert (D4 < 100 + 1000 * 1 bsl 3 , D4 ) % Extra 100 ms for reliability
145+
146+ catch
147+ C2 :R2 :S2 ->
148+ ok = application :stop (grisp_connect ),
149+ application :set_env (grisp_connect , connect , OldConectEnv ),
150+ erlang :raise (C2 , R2 , S2 )
151+ after
152+ grisp_connect_test_server :stop (Apps ),
153+ ? assertEqual ([], flush ())
154+ end ,
155+
156+ % Wait for grisp_connect to not be connected anymore
157+ fun WaitNotConnected () ->
158+ case grisp_connect :is_connected () of
159+ false -> ok ;
160+ true ->
161+ timer :sleep (50 ),
162+ WaitNotConnected ()
163+ end
164+ end (),
165+ T6 = os :timestamp (),
166+
167+ % Finally we test the delay is reset when reconnecting
168+
169+ grisp_connect_test_server :start (#{
170+ cert_dir => CertDir ,
171+ init_callback => fun (Req , Opts ) ->
172+ Self ! {CallRef , init , os :timestamp ()},
173+ {ok , cowboy_req :reply (400 , #{}, <<" Canceled" >>, Req ), Opts }
174+ end }),
175+ try
176+ T7 = receive
177+ {CallRef , init , V7 } -> V7
178+ after 2300 ->
179+ erlang :error (timeout7 )
180+ end ,
181+ T8 = receive
182+ {CallRef , init , V8 } -> V8
183+ after 4300 ->
184+ erlang :error (timeout8 )
185+ end ,
186+ D5 = timer :now_diff (T7 , T6 ) div 1000 ,
187+ D6 = timer :now_diff (T8 , T7 ) div 1000 ,
188+ ? assert (D5 > 900 , D5 ), % 100 ms less for reliability
189+ ? assert (D5 < 100 + 1000 * 1 bsl 1 , D5 ), % Extra 100 ms for reliability
190+ ? assert (D6 > 900 , D6 ), % 100 ms less for reliability
191+ ? assert (D6 < 100 + 1000 * 1 bsl 2 , D6 ) % Extra 100 ms for reliability
192+ after
193+ ok = application :stop (grisp_connect ),
194+ application :set_env (grisp_connect , connect , OldConectEnv ),
195+ grisp_connect_test_server :wait_disconnection (),
196+ ? assertEqual ([], flush ()),
197+ grisp_connect_test_server :stop (Apps )
198+ end ,
199+ ok .
200+
65201bad_client_version_test (_ ) ->
66202 CertDir = cert_dir (),
67203 Apps = grisp_connect_test_server :start (#{
@@ -70,12 +206,14 @@ bad_client_version_test(_) ->
70206 try
71207 {ok , _ } = application :ensure_all_started (grisp_emulation ),
72208 application :load (grisp_connect ),
209+ {ok , OldMaxRetryEnv } = application :get_env (grisp_connect , ws_max_retries ),
73210 application :set_env (grisp_connect , ws_max_retries , 2 ),
74211 {ok , _ } = application :ensure_all_started (grisp_connect ),
75212 try
76213 ? assertMatch ({error , ws_upgrade_failed }, wait_connection ())
77214 after
78- ok = application :stop (grisp_connect )
215+ ok = application :stop (grisp_connect ),
216+ application :set_env (grisp_connect , ws_max_retries , OldMaxRetryEnv )
79217 end
80218 after
81219 grisp_connect_test_server :wait_disconnection (),
@@ -92,13 +230,17 @@ bad_server_version_test(_) ->
92230 try
93231 {ok , _ } = application :ensure_all_started (grisp_emulation ),
94232 application :load (grisp_connect ),
233+ {ok , OldMaxRetryEnv } = application :get_env (grisp_connect , ws_max_retries ),
95234 application :set_env (grisp_connect , ws_max_retries , 2 ),
96235 {ok , _ } = application :ensure_all_started (grisp_connect ),
97236 try
98237 % There is no way to know the reason why gun closed the connection
99- ? assertMatch ({error , {closed , _ }}, wait_connection ())
238+ % but we don't want jarl to crash because the protocol couldn't be
239+ % negociated.
240+ ? assertMatch ({error , normal }, wait_connection ())
100241 after
101- ok = application :stop (grisp_connect )
242+ ok = application :stop (grisp_connect ),
243+ application :set_env (grisp_connect , ws_max_retries , OldMaxRetryEnv )
102244 end
103245 after
104246 grisp_connect_test_server :wait_disconnection (),
0 commit comments