1 /*
2     Copyright (c) 2017 Contributors as noted in the AUTHORS file
3 
4     This file is part of libzmq, the ZeroMQ core engine in C++.
5 
6     libzmq is free software; you can redistribute it and/or modify it under
7     the terms of the GNU Lesser General Public License (LGPL) as published
8     by the Free Software Foundation; either version 3 of the License, or
9     (at your option) any later version.
10 
11     As a special exception, the Contributors give you permission to link
12     this library with independent modules to produce an executable,
13     regardless of the license terms of these independent modules, and to
14     copy and distribute the resulting executable under terms of your choice,
15     provided that you also meet, for each linked independent module, the
16     terms and conditions of the license of that module. An independent
17     module is a module which is not derived from or based on this library.
18     If you modify this library, you must extend this exception to your
19     version of the library.
20 
21     libzmq is distributed in the hope that it will be useful, but WITHOUT
22     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23     FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24     License for more details.
25 
26     You should have received a copy of the GNU Lesser General Public License
27     along with this program.  If not, see <http://www.gnu.org/licenses/>.
28 */
29 #include <assert.h>
30 
31 #include "testutil.hpp"
32 #include "testutil_unity.hpp"
33 #include "testutil_monitoring.hpp"
34 
35 #include <unity.h>
36 
37 // test behavior with (mostly) default values
reconnect_default()38 void reconnect_default ()
39 {
40     // setup pub socket
41     void *pub = test_context_socket (ZMQ_PUB);
42     //  Bind pub socket
43     TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (pub, ENDPOINT_0));
44 
45     // setup sub socket
46     void *sub = test_context_socket (ZMQ_SUB);
47     //  Monitor all events on sub
48     TEST_ASSERT_SUCCESS_ERRNO (
49       zmq_socket_monitor (sub, "inproc://monitor-sub", ZMQ_EVENT_ALL));
50     //  Create socket for collecting monitor events
51     void *sub_mon = test_context_socket (ZMQ_PAIR);
52     //  Connect so they'll get events
53     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub_mon, "inproc://monitor-sub"));
54     // set reconnect interval so only a single reconnect is tried
55     int interval = 60 * 1000;
56     TEST_ASSERT_SUCCESS_ERRNO (
57       zmq_setsockopt (sub, ZMQ_RECONNECT_IVL, &interval, sizeof (interval)));
58     // connect to pub
59     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub, ENDPOINT_0));
60 
61     //  confirm that we get following events
62     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_DELAYED);
63     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECTED);
64     expect_monitor_event (sub_mon, ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
65 
66     // close the pub socket
67     test_context_socket_close_zero_linger (pub);
68 
69     //  confirm that we get following events
70     expect_monitor_event (sub_mon, ZMQ_EVENT_DISCONNECTED);
71     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_RETRIED);
72 
73     // ZMQ_EVENT_CONNECT_RETRIED should be last event, because of timeout set above
74     int event;
75     char *event_address;
76     int rc = get_monitor_event_with_timeout (sub_mon, &event, &event_address,
77                                              2 * 1000);
78     assert (rc == -1);
79 
80     //  Close sub
81     //  TODO why does this use zero_linger?
82     test_context_socket_close_zero_linger (sub);
83 
84     //  Close monitor
85     //  TODO why does this use zero_linger?
86     test_context_socket_close_zero_linger (sub_mon);
87 }
88 
89 
90 // test successful reconnect
reconnect_success()91 void reconnect_success ()
92 {
93     // setup pub socket
94     void *pub = test_context_socket (ZMQ_PUB);
95     //  Bind pub socket
96     TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (pub, ENDPOINT_0));
97 
98     // setup sub socket
99     void *sub = test_context_socket (ZMQ_SUB);
100     //  Monitor all events on sub
101     TEST_ASSERT_SUCCESS_ERRNO (
102       zmq_socket_monitor (sub, "inproc://monitor-sub", ZMQ_EVENT_ALL));
103     //  Create socket for collecting monitor events
104     void *sub_mon = test_context_socket (ZMQ_PAIR);
105     //  Connect so they'll get events
106     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub_mon, "inproc://monitor-sub"));
107     // set reconnect interval so only a single reconnect is tried
108     int interval = 1 * 1000;
109     TEST_ASSERT_SUCCESS_ERRNO (
110       zmq_setsockopt (sub, ZMQ_RECONNECT_IVL, &interval, sizeof (interval)));
111     // connect to pub
112     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub, ENDPOINT_0));
113 
114     //  confirm that we get following events
115     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_DELAYED);
116     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECTED);
117     expect_monitor_event (sub_mon, ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
118 
119     // close the pub socket
120     test_context_socket_close_zero_linger (pub);
121 
122     //  confirm that we get following events
123     expect_monitor_event (sub_mon, ZMQ_EVENT_DISCONNECTED);
124     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_RETRIED);
125 
126     // ZMQ_EVENT_CONNECT_RETRIED should be last event, because of timeout set above
127     int event;
128     char *event_address;
129     int rc = get_monitor_event_with_timeout (sub_mon, &event, &event_address,
130                                              SETTLE_TIME);
131     assert (rc == -1);
132 
133     //  Now re-bind pub socket and wait for re-connect
134     pub = test_context_socket (ZMQ_PUB);
135     TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (pub, ENDPOINT_0));
136     msleep (SETTLE_TIME);
137 
138     //  confirm that we get following events
139     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_DELAYED);
140     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECTED);
141     expect_monitor_event (sub_mon, ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
142 
143     // ZMQ_EVENT_HANDSHAKE_SUCCEEDED should be last event
144     rc = get_monitor_event_with_timeout (sub_mon, &event, &event_address,
145                                          SETTLE_TIME);
146     assert (rc == -1);
147 
148     //  Close sub
149     //  TODO why does this use zero_linger?
150     test_context_socket_close_zero_linger (sub);
151     test_context_socket_close_zero_linger (pub);
152 
153     //  Close monitor
154     //  TODO why does this use zero_linger?
155     test_context_socket_close_zero_linger (sub_mon);
156 }
157 
158 
159 #ifdef ZMQ_BUILD_DRAFT_API
160 // test stopping reconnect on connection refused
reconnect_stop_on_refused()161 void reconnect_stop_on_refused ()
162 {
163     // setup pub socket
164     void *pub = test_context_socket (ZMQ_PUB);
165     //  Bind pub socket
166     TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (pub, ENDPOINT_0));
167 
168     // setup sub socket
169     void *sub = test_context_socket (ZMQ_SUB);
170     //  Monitor all events on sub
171     TEST_ASSERT_SUCCESS_ERRNO (
172       zmq_socket_monitor (sub, "inproc://monitor-sub", ZMQ_EVENT_ALL));
173     //  Create socket for collecting monitor events
174     void *sub_mon = test_context_socket (ZMQ_PAIR);
175     //  Connect so they'll get events
176     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub_mon, "inproc://monitor-sub"));
177     // set option to stop reconnecting on error
178     int stopReconnectOnError = ZMQ_RECONNECT_STOP_CONN_REFUSED;
179     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (sub, ZMQ_RECONNECT_STOP,
180                                                &stopReconnectOnError,
181                                                sizeof (stopReconnectOnError)));
182     // connect to pub
183     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub, ENDPOINT_0));
184 
185     //  confirm that we get following events
186     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_DELAYED);
187     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECTED);
188     expect_monitor_event (sub_mon, ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
189 
190     // close the pub socket
191     test_context_socket_close_zero_linger (pub);
192 
193     //  confirm that we get following events
194     expect_monitor_event (sub_mon, ZMQ_EVENT_DISCONNECTED);
195     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_RETRIED);
196     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_DELAYED);
197     expect_monitor_event (sub_mon, ZMQ_EVENT_CLOSED);
198 
199     // ZMQ_EVENT_CLOSED should be last event, because of ZMQ_RECONNECT_STOP set above
200     int event = 0;
201     char *event_address;
202     int rc = get_monitor_event_with_timeout (sub_mon, &event, &event_address,
203                                              2 * 1000);
204     int limit = 0;
205     while ((rc != -1) && (++limit < 1000)) {
206         print_unexpected_event_stderr (event, rc, 0, -1);
207         rc = get_monitor_event_with_timeout (sub_mon, &event, &event_address,
208                                              2 * 1000);
209     }
210 
211     //  Close sub
212     //  TODO why does this use zero_linger?
213     test_context_socket_close_zero_linger (sub);
214 
215     //  Close monitor
216     //  TODO why does this use zero_linger?
217     test_context_socket_close_zero_linger (sub_mon);
218 }
219 #endif
220 
221 #ifdef ZMQ_BUILD_DRAFT_API
222 // test stopping reconnect on connection refused
reconnect_stop_on_handshake_failed()223 void reconnect_stop_on_handshake_failed ()
224 {
225     char bind_address[MAX_SOCKET_STRING];
226     size_t addr_length = sizeof (bind_address);
227     void *dummy = test_context_socket (ZMQ_STREAM);
228     TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (dummy, "tcp://127.0.0.1:0"));
229     TEST_ASSERT_SUCCESS_ERRNO (
230       zmq_getsockopt (dummy, ZMQ_LAST_ENDPOINT, bind_address, &addr_length));
231 
232     // setup sub socket
233     void *sub = test_context_socket (ZMQ_SUB);
234     //  Monitor all events on sub
235     TEST_ASSERT_SUCCESS_ERRNO (
236       zmq_socket_monitor (sub, "inproc://monitor-sub", ZMQ_EVENT_ALL));
237     //  Create socket for collecting monitor events
238     void *sub_mon = test_context_socket (ZMQ_PAIR);
239     //  Connect so they'll get events
240     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub_mon, "inproc://monitor-sub"));
241     // set handshake interval (i.e., timeout) to a more reasonable value
242     int handshakeInterval = 1000;
243     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (
244       sub, ZMQ_HANDSHAKE_IVL, &handshakeInterval, sizeof (handshakeInterval)));
245     // set option to stop reconnecting on failed handshake
246     int stopReconnectOnError = ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED;
247     TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (sub, ZMQ_RECONNECT_STOP,
248                                                &stopReconnectOnError,
249                                                sizeof (stopReconnectOnError)));
250     // connect to dummy stream socket above
251     TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sub, bind_address));
252 
253 #if 1
254     // ZMQ_EVENT_DISCONNECTED should be last event, because of ZMQ_RECONNECT_STOP set above
255     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECT_DELAYED);
256     expect_monitor_event (sub_mon, ZMQ_EVENT_CONNECTED);
257     expect_monitor_event (sub_mon, ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL);
258     expect_monitor_event (sub_mon, ZMQ_EVENT_DISCONNECTED);
259 #else
260     print_events (sub_mon, 2 * 1000, 1000);
261 #endif
262 
263     //  Close sub
264     //  TODO why does this use zero_linger?
265     test_context_socket_close_zero_linger (sub);
266     test_context_socket_close_zero_linger (dummy);
267 
268     //  Close monitor
269     //  TODO why does this use zero_linger?
270     test_context_socket_close_zero_linger (sub_mon);
271 }
272 #endif
273 
setUp()274 void setUp ()
275 {
276     setup_test_context ();
277 }
278 
tearDown()279 void tearDown ()
280 {
281     teardown_test_context ();
282 }
283 
main(void)284 int main (void)
285 {
286     setup_test_environment ();
287 
288     UNITY_BEGIN ();
289 
290     RUN_TEST (reconnect_default);
291     RUN_TEST (reconnect_success);
292 #ifdef ZMQ_BUILD_DRAFT_API
293     RUN_TEST (reconnect_stop_on_refused);
294     RUN_TEST (reconnect_stop_on_handshake_failed);
295 #endif
296     return UNITY_END ();
297 }
298