1 | /* |
2 | Copyright (c) 2007-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 | |
30 | #include "testutil.hpp" |
31 | #include "testutil_unity.hpp" |
32 | |
33 | #include <string.h> |
34 | |
35 | SETUP_TEARDOWN_TESTCONTEXT |
36 | |
37 | #ifdef ZMQ_BUILD_DRAFT_API |
38 | bool send_msg_to_peer_if_ready (void *router_, const char *peer_routing_id_) |
39 | { |
40 | int rc = TEST_ASSERT_SUCCESS_MESSAGE_ERRNO ( |
41 | zmq_socket_get_peer_state (router_, peer_routing_id_, 1), |
42 | peer_routing_id_); |
43 | if (rc & ZMQ_POLLOUT) { |
44 | send_string_expect_success (router_, peer_routing_id_, |
45 | ZMQ_SNDMORE | ZMQ_DONTWAIT); |
46 | send_string_expect_success (router_, "Hello" , ZMQ_DONTWAIT); |
47 | |
48 | return true; |
49 | } |
50 | return false; |
51 | } |
52 | #endif |
53 | |
54 | void test_get_peer_state () |
55 | { |
56 | #ifdef ZMQ_BUILD_DRAFT_API |
57 | void *router = test_context_socket (ZMQ_ROUTER); |
58 | |
59 | int mandatory = 1; |
60 | TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (router, ZMQ_ROUTER_MANDATORY, |
61 | &mandatory, sizeof (mandatory))); |
62 | |
63 | const char *my_endpoint = "inproc://test_get_peer_state" ; |
64 | TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (router, my_endpoint)); |
65 | |
66 | void *dealer1 = test_context_socket (ZMQ_DEALER); |
67 | void *dealer2 = test_context_socket (ZMQ_DEALER); |
68 | |
69 | // Lower HWMs to allow doing the test with fewer messages |
70 | const int hwm = 100; |
71 | TEST_ASSERT_SUCCESS_ERRNO ( |
72 | zmq_setsockopt (router, ZMQ_SNDHWM, &hwm, sizeof (int))); |
73 | TEST_ASSERT_SUCCESS_ERRNO ( |
74 | zmq_setsockopt (dealer1, ZMQ_RCVHWM, &hwm, sizeof (int))); |
75 | TEST_ASSERT_SUCCESS_ERRNO ( |
76 | zmq_setsockopt (dealer2, ZMQ_RCVHWM, &hwm, sizeof (int))); |
77 | |
78 | const char *dealer1_routing_id = "X" ; |
79 | const char *dealer2_routing_id = "Y" ; |
80 | |
81 | // Name dealer1 "X" and connect it to our router |
82 | TEST_ASSERT_SUCCESS_ERRNO ( |
83 | zmq_setsockopt (dealer1, ZMQ_ROUTING_ID, dealer1_routing_id, 1)); |
84 | TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (dealer1, my_endpoint)); |
85 | |
86 | // Name dealer2 "Y" and connect it to our router |
87 | TEST_ASSERT_SUCCESS_ERRNO ( |
88 | zmq_setsockopt (dealer2, ZMQ_ROUTING_ID, dealer2_routing_id, 1)); |
89 | TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (dealer2, my_endpoint)); |
90 | |
91 | // Get message from both dealers to know when connection is ready |
92 | send_string_expect_success (dealer1, "Hello" , 0); |
93 | recv_string_expect_success (router, dealer1_routing_id, 0); |
94 | recv_string_expect_success (router, "Hello" , 0); |
95 | |
96 | send_string_expect_success (dealer2, "Hello" , 0); |
97 | recv_string_expect_success (router, dealer2_routing_id, 0); |
98 | recv_string_expect_success (router, "Hello" , 0); |
99 | |
100 | void *poller = zmq_poller_new (); |
101 | TEST_ASSERT_NOT_NULL (poller); |
102 | |
103 | // Poll on router and dealer1, but not on dealer2 |
104 | TEST_ASSERT_SUCCESS_ERRNO ( |
105 | zmq_poller_add (poller, router, NULL, ZMQ_POLLOUT)); |
106 | TEST_ASSERT_SUCCESS_ERRNO ( |
107 | zmq_poller_add (poller, dealer1, NULL, ZMQ_POLLIN)); |
108 | |
109 | const unsigned int count = 10000; |
110 | const unsigned int event_size = 2; |
111 | bool dealer2_blocked = false; |
112 | unsigned int dealer1_sent = 0, dealer2_sent = 0, dealer1_received = 0; |
113 | zmq_poller_event_t events[event_size]; |
114 | for (unsigned int iteration = 0; iteration < count; ++iteration) { |
115 | TEST_ASSERT_SUCCESS_ERRNO ( |
116 | zmq_poller_wait_all (poller, events, event_size, -1)); |
117 | for (unsigned int event_no = 0; event_no < event_size; ++event_no) { |
118 | const zmq_poller_event_t ¤t_event = events[event_no]; |
119 | if (current_event.socket == router |
120 | && current_event.events & ZMQ_POLLOUT) { |
121 | if (send_msg_to_peer_if_ready (router, dealer1_routing_id)) |
122 | ++dealer1_sent; |
123 | |
124 | if (send_msg_to_peer_if_ready (router, dealer2_routing_id)) |
125 | ++dealer2_sent; |
126 | else |
127 | dealer2_blocked = true; |
128 | } |
129 | if (current_event.socket == dealer1 |
130 | && current_event.events & ZMQ_POLLIN) { |
131 | recv_string_expect_success (dealer1, "Hello" , ZMQ_DONTWAIT); |
132 | int more; |
133 | size_t more_size = sizeof (more); |
134 | TEST_ASSERT_SUCCESS_ERRNO ( |
135 | zmq_getsockopt (dealer1, ZMQ_RCVMORE, &more, &more_size)); |
136 | TEST_ASSERT_FALSE (more); |
137 | |
138 | ++dealer1_received; |
139 | } |
140 | // never read from dealer2, so its pipe becomes full eventually |
141 | } |
142 | } |
143 | printf ("dealer1_sent = %u, dealer2_sent = %u, dealer1_received = %u\n" , |
144 | dealer1_sent, dealer2_sent, dealer1_received); |
145 | TEST_ASSERT_TRUE (dealer2_blocked); |
146 | zmq_poller_destroy (&poller); |
147 | |
148 | test_context_socket_close (router); |
149 | test_context_socket_close (dealer1); |
150 | test_context_socket_close (dealer2); |
151 | #endif |
152 | } |
153 | |
154 | void test_get_peer_state_corner_cases () |
155 | { |
156 | #ifdef ZMQ_BUILD_DRAFT_API |
157 | const char peer_routing_id[] = "foo" ; |
158 | |
159 | // call get_peer_state with NULL socket |
160 | int rc = zmq_socket_get_peer_state (NULL, peer_routing_id, |
161 | strlen (peer_routing_id)); |
162 | TEST_ASSERT_EQUAL_INT (-1, rc); |
163 | TEST_ASSERT_EQUAL_INT (ENOTSOCK, errno); |
164 | |
165 | void *dealer = test_context_socket (ZMQ_DEALER); |
166 | void *router = test_context_socket (ZMQ_ROUTER); |
167 | |
168 | // call get_peer_state with a non-ROUTER socket |
169 | rc = zmq_socket_get_peer_state (dealer, peer_routing_id, |
170 | strlen (peer_routing_id)); |
171 | TEST_ASSERT_EQUAL_INT (-1, rc); |
172 | TEST_ASSERT_EQUAL_INT (ENOTSUP, errno); |
173 | |
174 | // call get_peer_state for an unknown routing id |
175 | rc = zmq_socket_get_peer_state (router, peer_routing_id, |
176 | strlen (peer_routing_id)); |
177 | TEST_ASSERT_EQUAL_INT (-1, rc); |
178 | TEST_ASSERT_EQUAL_INT (EHOSTUNREACH, errno); |
179 | |
180 | test_context_socket_close (router); |
181 | test_context_socket_close (dealer); |
182 | #endif |
183 | } |
184 | |
185 | void test_basic () |
186 | { |
187 | char my_endpoint[MAX_SOCKET_STRING]; |
188 | void *router = test_context_socket (ZMQ_ROUTER); |
189 | bind_loopback_ipv4 (router, my_endpoint, sizeof my_endpoint); |
190 | |
191 | // Send a message to an unknown peer with the default setting |
192 | // This will not report any error |
193 | send_string_expect_success (router, "UNKNOWN" , ZMQ_SNDMORE); |
194 | send_string_expect_success (router, "DATA" , 0); |
195 | |
196 | // Send a message to an unknown peer with mandatory routing |
197 | // This will fail |
198 | int mandatory = 1; |
199 | TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (router, ZMQ_ROUTER_MANDATORY, |
200 | &mandatory, sizeof (mandatory))); |
201 | int rc = zmq_send (router, "UNKNOWN" , 7, ZMQ_SNDMORE); |
202 | TEST_ASSERT_EQUAL_INT (-1, rc); |
203 | TEST_ASSERT_EQUAL_INT (EHOSTUNREACH, errno); |
204 | |
205 | // Create dealer called "X" and connect it to our router |
206 | void *dealer = test_context_socket (ZMQ_DEALER); |
207 | TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (dealer, ZMQ_ROUTING_ID, "X" , 1)); |
208 | TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (dealer, my_endpoint)); |
209 | |
210 | // Get message from dealer to know when connection is ready |
211 | send_string_expect_success (dealer, "Hello" , 0); |
212 | recv_string_expect_success (router, "X" , 0); |
213 | |
214 | // Send a message to connected dealer now |
215 | // It should work |
216 | send_string_expect_success (router, "X" , ZMQ_SNDMORE); |
217 | send_string_expect_success (router, "Hello" , 0); |
218 | |
219 | test_context_socket_close (router); |
220 | test_context_socket_close (dealer); |
221 | } |
222 | |
223 | int main (void) |
224 | { |
225 | setup_test_environment (); |
226 | |
227 | UNITY_BEGIN (); |
228 | RUN_TEST (test_basic); |
229 | RUN_TEST (test_get_peer_state); |
230 | RUN_TEST (test_get_peer_state_corner_cases); |
231 | |
232 | return UNITY_END (); |
233 | } |
234 | |