xref: /libuv/test/test-ping-pong.c (revision 011a1ac1)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "task.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h> /* strlen */
28 
29 static int completed_pingers = 0;
30 
31 #if defined(__CYGWIN__) || defined(__MSYS__) || defined(__MVS__)
32 #define NUM_PINGS 100 /* fewer pings to avoid timeout */
33 #else
34 #define NUM_PINGS 1000
35 #endif
36 
37 static char PING[] = "PING\n";
38 static char PONG[] = "PONG\n";
39 static int pinger_on_connect_count;
40 
41 
42 typedef struct {
43   int vectored_writes;
44   unsigned pongs;
45   unsigned state;
46   union {
47     uv_tcp_t tcp;
48     uv_pipe_t pipe;
49   } stream;
50   uv_connect_t connect_req;
51   char* pong;
52 } pinger_t;
53 
54 
alloc_cb(uv_handle_t * handle,size_t size,uv_buf_t * buf)55 static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
56   buf->base = malloc(size);
57   buf->len = size;
58 }
59 
60 
ponger_on_close(uv_handle_t * handle)61 static void ponger_on_close(uv_handle_t* handle) {
62   if (handle->data)
63     free(handle->data);
64   else
65     free(handle);
66 }
67 
68 
pinger_on_close(uv_handle_t * handle)69 static void pinger_on_close(uv_handle_t* handle) {
70   pinger_t* pinger = (pinger_t*) handle->data;
71 
72   ASSERT_EQ(NUM_PINGS, pinger->pongs);
73 
74   if (handle == (uv_handle_t*) &pinger->stream.tcp) {
75     free(pinger); /* also frees handle */
76   } else {
77     uv_close((uv_handle_t*) &pinger->stream.tcp, ponger_on_close);
78     free(handle);
79   }
80 
81   completed_pingers++;
82 }
83 
84 
pinger_after_write(uv_write_t * req,int status)85 static void pinger_after_write(uv_write_t* req, int status) {
86   ASSERT_OK(status);
87   free(req->data);
88   free(req);
89 }
90 
91 
pinger_write_ping(pinger_t * pinger)92 static void pinger_write_ping(pinger_t* pinger) {
93   uv_stream_t* stream;
94   uv_write_t* req;
95   uv_buf_t bufs[sizeof PING - 1];
96   int i, nbufs;
97 
98   stream = (uv_stream_t*) &pinger->stream.tcp;
99 
100   if (!pinger->vectored_writes) {
101     /* Write a single buffer. */
102     nbufs = 1;
103     bufs[0] = uv_buf_init(PING, sizeof PING - 1);
104   } else {
105     /* Write multiple buffers, each with one byte in them. */
106     nbufs = sizeof PING - 1;
107     for (i = 0; i < nbufs; i++) {
108       bufs[i] = uv_buf_init(&PING[i], 1);
109     }
110   }
111 
112   req = malloc(sizeof(*req));
113   ASSERT_NOT_NULL(req);
114   req->data = NULL;
115   ASSERT_OK(uv_write(req, stream, bufs, nbufs, pinger_after_write));
116 
117   puts("PING");
118 }
119 
120 
pinger_read_cb(uv_stream_t * stream,ssize_t nread,const uv_buf_t * buf)121 static void pinger_read_cb(uv_stream_t* stream,
122                            ssize_t nread,
123                            const uv_buf_t* buf) {
124   ssize_t i;
125   pinger_t* pinger;
126 
127   pinger = (pinger_t*) stream->data;
128 
129   if (nread < 0) {
130     ASSERT_EQ(nread, UV_EOF);
131 
132     puts("got EOF");
133     free(buf->base);
134 
135     uv_close((uv_handle_t*) stream, pinger_on_close);
136 
137     return;
138   }
139 
140   /* Now we count the pongs */
141   for (i = 0; i < nread; i++) {
142     ASSERT_EQ(buf->base[i], pinger->pong[pinger->state]);
143     pinger->state = (pinger->state + 1) % strlen(pinger->pong);
144 
145     if (pinger->state != 0)
146       continue;
147 
148     printf("PONG %d\n", pinger->pongs);
149     pinger->pongs++;
150 
151     if (pinger->pongs < NUM_PINGS) {
152       pinger_write_ping(pinger);
153     } else {
154       uv_close((uv_handle_t*) stream, pinger_on_close);
155       break;
156     }
157   }
158 
159   free(buf->base);
160 }
161 
162 
ponger_read_cb(uv_stream_t * stream,ssize_t nread,const uv_buf_t * buf)163 static void ponger_read_cb(uv_stream_t* stream,
164                            ssize_t nread,
165                            const uv_buf_t* buf) {
166   uv_buf_t writebuf;
167   uv_write_t* req;
168   int i;
169 
170   if (nread < 0) {
171     ASSERT_EQ(nread, UV_EOF);
172 
173     puts("got EOF");
174     free(buf->base);
175 
176     uv_close((uv_handle_t*) stream, ponger_on_close);
177 
178     return;
179   }
180 
181   /* Echo back */
182   for (i = 0; i < nread; i++) {
183     if (buf->base[i] == 'I')
184       buf->base[i] = 'O';
185   }
186 
187   writebuf = uv_buf_init(buf->base, nread);
188   req = malloc(sizeof(*req));
189   ASSERT_NOT_NULL(req);
190   req->data = buf->base;
191   ASSERT_OK(uv_write(req, stream, &writebuf, 1, pinger_after_write));
192 }
193 
194 
pinger_on_connect(uv_connect_t * req,int status)195 static void pinger_on_connect(uv_connect_t* req, int status) {
196   pinger_t* pinger = (pinger_t*) req->handle->data;
197 
198   pinger_on_connect_count++;
199 
200   ASSERT_OK(status);
201 
202   ASSERT_EQ(1, uv_is_readable(req->handle));
203   ASSERT_EQ(1, uv_is_writable(req->handle));
204   ASSERT_OK(uv_is_closing((uv_handle_t *) req->handle));
205 
206   pinger_write_ping(pinger);
207 
208   ASSERT_OK(uv_read_start((uv_stream_t*) req->handle,
209                           alloc_cb,
210                           pinger_read_cb));
211 }
212 
213 
214 /* same ping-pong test, but using IPv6 connection */
tcp_pinger_v6_new(int vectored_writes)215 static void tcp_pinger_v6_new(int vectored_writes) {
216   int r;
217   struct sockaddr_in6 server_addr;
218   pinger_t* pinger;
219 
220 
221   ASSERT_OK(uv_ip6_addr("::1", TEST_PORT, &server_addr));
222   pinger = malloc(sizeof(*pinger));
223   ASSERT_NOT_NULL(pinger);
224   pinger->vectored_writes = vectored_writes;
225   pinger->state = 0;
226   pinger->pongs = 0;
227   pinger->pong = PING;
228 
229   /* Try to connect to the server and do NUM_PINGS ping-pongs. */
230   r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp);
231   pinger->stream.tcp.data = pinger;
232   ASSERT_OK(r);
233 
234   /* We are never doing multiple reads/connects at a time anyway, so these
235    * handles can be pre-initialized. */
236   r = uv_tcp_connect(&pinger->connect_req,
237                      &pinger->stream.tcp,
238                      (const struct sockaddr*) &server_addr,
239                      pinger_on_connect);
240   ASSERT_OK(r);
241 
242   /* Synchronous connect callbacks are not allowed. */
243   ASSERT_OK(pinger_on_connect_count);
244 }
245 
246 
tcp_pinger_new(int vectored_writes)247 static void tcp_pinger_new(int vectored_writes) {
248   int r;
249   struct sockaddr_in server_addr;
250   pinger_t* pinger;
251 
252   ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr));
253   pinger = malloc(sizeof(*pinger));
254   ASSERT_NOT_NULL(pinger);
255   pinger->vectored_writes = vectored_writes;
256   pinger->state = 0;
257   pinger->pongs = 0;
258   pinger->pong = PING;
259 
260   /* Try to connect to the server and do NUM_PINGS ping-pongs. */
261   r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp);
262   pinger->stream.tcp.data = pinger;
263   ASSERT_OK(r);
264 
265   /* We are never doing multiple reads/connects at a time anyway, so these
266    * handles can be pre-initialized. */
267   r = uv_tcp_connect(&pinger->connect_req,
268                      &pinger->stream.tcp,
269                      (const struct sockaddr*) &server_addr,
270                      pinger_on_connect);
271   ASSERT_OK(r);
272 
273   /* Synchronous connect callbacks are not allowed. */
274   ASSERT_OK(pinger_on_connect_count);
275 }
276 
277 
pipe_pinger_new(int vectored_writes)278 static void pipe_pinger_new(int vectored_writes) {
279   int r;
280   pinger_t* pinger;
281 
282   pinger = malloc(sizeof(*pinger));
283   ASSERT_NOT_NULL(pinger);
284   pinger->vectored_writes = vectored_writes;
285   pinger->state = 0;
286   pinger->pongs = 0;
287   pinger->pong = PING;
288 
289   /* Try to connect to the server and do NUM_PINGS ping-pongs. */
290   r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0);
291   pinger->stream.pipe.data = pinger;
292   ASSERT_OK(r);
293 
294   /* We are never doing multiple reads/connects at a time anyway, so these
295    * handles can be pre-initialized. */
296   uv_pipe_connect(&pinger->connect_req, &pinger->stream.pipe, TEST_PIPENAME,
297       pinger_on_connect);
298 
299   /* Synchronous connect callbacks are not allowed. */
300   ASSERT_OK(pinger_on_connect_count);
301 }
302 
303 
socketpair_pinger_new(int vectored_writes)304 static void socketpair_pinger_new(int vectored_writes) {
305   pinger_t* pinger;
306   uv_os_sock_t fds[2];
307   uv_tcp_t* ponger;
308 
309   pinger = malloc(sizeof(*pinger));
310   ASSERT_NOT_NULL(pinger);
311   pinger->vectored_writes = vectored_writes;
312   pinger->state = 0;
313   pinger->pongs = 0;
314   pinger->pong = PONG;
315 
316   /* Try to make a socketpair and do NUM_PINGS ping-pongs. */
317   (void)uv_default_loop(); /* ensure WSAStartup has been performed */
318   ASSERT_OK(uv_socketpair(SOCK_STREAM, 0, fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE));
319 #ifndef _WIN32
320   /* On Windows, this is actually a UV_TCP, but libuv doesn't detect that. */
321   ASSERT_EQ(uv_guess_handle((uv_file) fds[0]), UV_NAMED_PIPE);
322   ASSERT_EQ(uv_guess_handle((uv_file) fds[1]), UV_NAMED_PIPE);
323 #endif
324 
325   ASSERT_OK(uv_tcp_init(uv_default_loop(), &pinger->stream.tcp));
326   pinger->stream.pipe.data = pinger;
327   ASSERT_OK(uv_tcp_open(&pinger->stream.tcp, fds[1]));
328 
329   ponger = malloc(sizeof(*ponger));
330   ASSERT_NOT_NULL(ponger);
331   ponger->data = NULL;
332   ASSERT_OK(uv_tcp_init(uv_default_loop(), ponger));
333   ASSERT_OK(uv_tcp_open(ponger, fds[0]));
334 
335   pinger_write_ping(pinger);
336 
337   ASSERT_OK(uv_read_start((uv_stream_t*) &pinger->stream.tcp,
338                           alloc_cb,
339                           pinger_read_cb));
340   ASSERT_OK(uv_read_start((uv_stream_t*) ponger,
341                           alloc_cb,
342                           ponger_read_cb));
343 }
344 
345 
pipe2_pinger_new(int vectored_writes)346 static void pipe2_pinger_new(int vectored_writes) {
347   uv_file fds[2];
348   pinger_t* pinger;
349   uv_pipe_t* ponger;
350 
351   /* Try to make a pipe and do NUM_PINGS pings. */
352   ASSERT_OK(uv_pipe(fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE));
353   ASSERT_EQ(uv_guess_handle(fds[0]), UV_NAMED_PIPE);
354   ASSERT_EQ(uv_guess_handle(fds[1]), UV_NAMED_PIPE);
355 
356   ponger = malloc(sizeof(*ponger));
357   ASSERT_NOT_NULL(ponger);
358   ASSERT_OK(uv_pipe_init(uv_default_loop(), ponger, 0));
359   ASSERT_OK(uv_pipe_open(ponger, fds[0]));
360 
361   pinger = malloc(sizeof(*pinger));
362   ASSERT_NOT_NULL(pinger);
363   pinger->vectored_writes = vectored_writes;
364   pinger->state = 0;
365   pinger->pongs = 0;
366   pinger->pong = PING;
367   ASSERT_OK(uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0));
368   ASSERT_OK(uv_pipe_open(&pinger->stream.pipe, fds[1]));
369   pinger->stream.pipe.data = pinger; /* record for close_cb */
370   ponger->data = pinger; /* record for read_cb */
371 
372   pinger_write_ping(pinger);
373 
374   ASSERT_OK(uv_read_start((uv_stream_t*) ponger, alloc_cb, pinger_read_cb));
375 }
376 
run_ping_pong_test(void)377 static int run_ping_pong_test(void) {
378   uv_run(uv_default_loop(), UV_RUN_DEFAULT);
379   ASSERT_EQ(1, completed_pingers);
380 
381   MAKE_VALGRIND_HAPPY(uv_default_loop());
382   return 0;
383 }
384 
385 
TEST_IMPL(tcp_ping_pong)386 TEST_IMPL(tcp_ping_pong) {
387   tcp_pinger_new(0);
388   run_ping_pong_test();
389 
390   completed_pingers = 0;
391   socketpair_pinger_new(0);
392   return run_ping_pong_test();
393 }
394 
395 
TEST_IMPL(tcp_ping_pong_vec)396 TEST_IMPL(tcp_ping_pong_vec) {
397   tcp_pinger_new(1);
398   run_ping_pong_test();
399 
400   completed_pingers = 0;
401   socketpair_pinger_new(1);
402   return run_ping_pong_test();
403 }
404 
405 
TEST_IMPL(tcp6_ping_pong)406 TEST_IMPL(tcp6_ping_pong) {
407   if (!can_ipv6())
408     RETURN_SKIP("IPv6 not supported");
409   tcp_pinger_v6_new(0);
410   return run_ping_pong_test();
411 }
412 
413 
TEST_IMPL(tcp6_ping_pong_vec)414 TEST_IMPL(tcp6_ping_pong_vec) {
415   if (!can_ipv6())
416     RETURN_SKIP("IPv6 not supported");
417   tcp_pinger_v6_new(1);
418   return run_ping_pong_test();
419 }
420 
421 
TEST_IMPL(pipe_ping_pong)422 TEST_IMPL(pipe_ping_pong) {
423   pipe_pinger_new(0);
424   run_ping_pong_test();
425 
426   completed_pingers = 0;
427   pipe2_pinger_new(0);
428   return run_ping_pong_test();
429 }
430 
431 
TEST_IMPL(pipe_ping_pong_vec)432 TEST_IMPL(pipe_ping_pong_vec) {
433   pipe_pinger_new(1);
434   run_ping_pong_test();
435 
436   completed_pingers = 0;
437   pipe2_pinger_new(1);
438   return run_ping_pong_test();
439 }
440