@@ -34,7 +34,8 @@ all() ->
34
34
35
35
init_per_testcase (TestCase , Config )
36
36
when TestCase =:= bad_client_version_test ;
37
- TestCase =:= bad_server_version_test ->
37
+ TestCase =:= bad_server_version_test ;
38
+ TestCase =:= exponential_backoff_test ->
38
39
Config ;
39
40
init_per_testcase (TestCase , Config ) ->
40
41
CertDir = cert_dir (),
@@ -51,7 +52,8 @@ init_per_testcase(TestCase, Config) ->
51
52
52
53
end_per_testcase (TestCase , Config )
53
54
when TestCase =:= bad_client_version_test ;
54
- TestCase =:= bad_server_version_test ->
55
+ TestCase =:= bad_server_version_test ;
56
+ TestCase =:= exponential_backoff_test ->
55
57
Config ;
56
58
end_per_testcase (_ , Config ) ->
57
59
ok = application :stop (grisp_connect ),
@@ -62,6 +64,140 @@ end_per_testcase(_, Config) ->
62
64
63
65
% --- Tests ---------------------------------------------------------------------
64
66
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
+
65
201
bad_client_version_test (_ ) ->
66
202
CertDir = cert_dir (),
67
203
Apps = grisp_connect_test_server :start (#{
@@ -70,12 +206,14 @@ bad_client_version_test(_) ->
70
206
try
71
207
{ok , _ } = application :ensure_all_started (grisp_emulation ),
72
208
application :load (grisp_connect ),
209
+ {ok , OldMaxRetryEnv } = application :get_env (grisp_connect , ws_max_retries ),
73
210
application :set_env (grisp_connect , ws_max_retries , 2 ),
74
211
{ok , _ } = application :ensure_all_started (grisp_connect ),
75
212
try
76
213
? assertMatch ({error , ws_upgrade_failed }, wait_connection ())
77
214
after
78
- ok = application :stop (grisp_connect )
215
+ ok = application :stop (grisp_connect ),
216
+ application :set_env (grisp_connect , ws_max_retries , OldMaxRetryEnv )
79
217
end
80
218
after
81
219
grisp_connect_test_server :wait_disconnection (),
@@ -92,13 +230,17 @@ bad_server_version_test(_) ->
92
230
try
93
231
{ok , _ } = application :ensure_all_started (grisp_emulation ),
94
232
application :load (grisp_connect ),
233
+ {ok , OldMaxRetryEnv } = application :get_env (grisp_connect , ws_max_retries ),
95
234
application :set_env (grisp_connect , ws_max_retries , 2 ),
96
235
{ok , _ } = application :ensure_all_started (grisp_connect ),
97
236
try
98
237
% 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 ())
100
241
after
101
- ok = application :stop (grisp_connect )
242
+ ok = application :stop (grisp_connect ),
243
+ application :set_env (grisp_connect , ws_max_retries , OldMaxRetryEnv )
102
244
end
103
245
after
104
246
grisp_connect_test_server :wait_disconnection (),
0 commit comments