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
28static int completed_pingers = 0;
29
30#if defined(__CYGWIN__) || defined(__MSYS__) || defined(__MVS__)
31#define NUM_PINGS 100 /* fewer pings to avoid timeout */
32#else
33#define NUM_PINGS 1000
34#endif
35
36/* 64 bytes is enough for a pinger */
37#define BUFSIZE 10240
38
39static char PING[] = "PING\n";
40static int pinger_on_connect_count;
41
42
43typedef struct {
44 int vectored_writes;
45 int pongs;
46 int state;
47 union {
48 uv_tcp_t tcp;
49 uv_pipe_t pipe;
50 } stream;
51 uv_connect_t connect_req;
52 char read_buffer[BUFSIZE];
53} pinger_t;
54
55
56static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
57 buf->base = malloc(size);
58 buf->len = size;
59}
60
61
62static void pinger_on_close(uv_handle_t* handle) {
63 pinger_t* pinger = (pinger_t*)handle->data;
64
65 ASSERT(NUM_PINGS == pinger->pongs);
66
67 free(pinger);
68
69 completed_pingers++;
70}
71
72
73static void pinger_after_write(uv_write_t *req, int status) {
74 ASSERT(status == 0);
75 free(req);
76}
77
78
79static void pinger_write_ping(pinger_t* pinger) {
80 uv_write_t *req;
81 uv_buf_t bufs[sizeof PING - 1];
82 int i, nbufs;
83
84 if (!pinger->vectored_writes) {
85 /* Write a single buffer. */
86 nbufs = 1;
87 bufs[0] = uv_buf_init(PING, sizeof PING - 1);
88 } else {
89 /* Write multiple buffers, each with one byte in them. */
90 nbufs = sizeof PING - 1;
91 for (i = 0; i < nbufs; i++) {
92 bufs[i] = uv_buf_init(&PING[i], 1);
93 }
94 }
95
96 req = malloc(sizeof(*req));
97 if (uv_write(req,
98 (uv_stream_t*) &pinger->stream.tcp,
99 bufs,
100 nbufs,
101 pinger_after_write)) {
102 FATAL("uv_write failed");
103 }
104
105 puts("PING");
106}
107
108
109static void pinger_read_cb(uv_stream_t* stream,
110 ssize_t nread,
111 const uv_buf_t* buf) {
112 ssize_t i;
113 pinger_t* pinger;
114
115 pinger = (pinger_t*)stream->data;
116
117 if (nread < 0) {
118 ASSERT(nread == UV_EOF);
119
120 puts("got EOF");
121 free(buf->base);
122
123 uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close);
124
125 return;
126 }
127
128 /* Now we count the pings */
129 for (i = 0; i < nread; i++) {
130 ASSERT(buf->base[i] == PING[pinger->state]);
131 pinger->state = (pinger->state + 1) % (sizeof(PING) - 1);
132
133 if (pinger->state != 0)
134 continue;
135
136 printf("PONG %d\n", pinger->pongs);
137 pinger->pongs++;
138
139 if (pinger->pongs < NUM_PINGS) {
140 pinger_write_ping(pinger);
141 } else {
142 uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close);
143 break;
144 }
145 }
146
147 free(buf->base);
148}
149
150
151static void pinger_on_connect(uv_connect_t *req, int status) {
152 pinger_t *pinger = (pinger_t*)req->handle->data;
153
154 pinger_on_connect_count++;
155
156 ASSERT(status == 0);
157
158 ASSERT(1 == uv_is_readable(req->handle));
159 ASSERT(1 == uv_is_writable(req->handle));
160 ASSERT(0 == uv_is_closing((uv_handle_t *) req->handle));
161
162 pinger_write_ping(pinger);
163
164 uv_read_start((uv_stream_t*)(req->handle), alloc_cb, pinger_read_cb);
165}
166
167
168/* same ping-pong test, but using IPv6 connection */
169static void tcp_pinger_v6_new(int vectored_writes) {
170 int r;
171 struct sockaddr_in6 server_addr;
172 pinger_t *pinger;
173
174
175 ASSERT(0 ==uv_ip6_addr("::1", TEST_PORT, &server_addr));
176 pinger = malloc(sizeof(*pinger));
177 ASSERT(pinger != NULL);
178 pinger->vectored_writes = vectored_writes;
179 pinger->state = 0;
180 pinger->pongs = 0;
181
182 /* Try to connect to the server and do NUM_PINGS ping-pongs. */
183 r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp);
184 pinger->stream.tcp.data = pinger;
185 ASSERT(!r);
186
187 /* We are never doing multiple reads/connects at a time anyway, so these
188 * handles can be pre-initialized. */
189 r = uv_tcp_connect(&pinger->connect_req,
190 &pinger->stream.tcp,
191 (const struct sockaddr*) &server_addr,
192 pinger_on_connect);
193 ASSERT(!r);
194
195 /* Synchronous connect callbacks are not allowed. */
196 ASSERT(pinger_on_connect_count == 0);
197}
198
199
200static void tcp_pinger_new(int vectored_writes) {
201 int r;
202 struct sockaddr_in server_addr;
203 pinger_t *pinger;
204
205 ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr));
206 pinger = malloc(sizeof(*pinger));
207 ASSERT(pinger != NULL);
208 pinger->vectored_writes = vectored_writes;
209 pinger->state = 0;
210 pinger->pongs = 0;
211
212 /* Try to connect to the server and do NUM_PINGS ping-pongs. */
213 r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp);
214 pinger->stream.tcp.data = pinger;
215 ASSERT(!r);
216
217 /* We are never doing multiple reads/connects at a time anyway, so these
218 * handles can be pre-initialized. */
219 r = uv_tcp_connect(&pinger->connect_req,
220 &pinger->stream.tcp,
221 (const struct sockaddr*) &server_addr,
222 pinger_on_connect);
223 ASSERT(!r);
224
225 /* Synchronous connect callbacks are not allowed. */
226 ASSERT(pinger_on_connect_count == 0);
227}
228
229
230static void pipe_pinger_new(int vectored_writes) {
231 int r;
232 pinger_t *pinger;
233
234 pinger = (pinger_t*)malloc(sizeof(*pinger));
235 ASSERT(pinger != NULL);
236 pinger->vectored_writes = vectored_writes;
237 pinger->state = 0;
238 pinger->pongs = 0;
239
240 /* Try to connect to the server and do NUM_PINGS ping-pongs. */
241 r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0);
242 pinger->stream.pipe.data = pinger;
243 ASSERT(!r);
244
245 /* We are never doing multiple reads/connects at a time anyway, so these
246 * handles can be pre-initialized. */
247 uv_pipe_connect(&pinger->connect_req, &pinger->stream.pipe, TEST_PIPENAME,
248 pinger_on_connect);
249
250 /* Synchronous connect callbacks are not allowed. */
251 ASSERT(pinger_on_connect_count == 0);
252}
253
254
255static int run_ping_pong_test(void) {
256 uv_run(uv_default_loop(), UV_RUN_DEFAULT);
257 ASSERT(completed_pingers == 1);
258
259 MAKE_VALGRIND_HAPPY();
260 return 0;
261}
262
263
264TEST_IMPL(tcp_ping_pong) {
265 tcp_pinger_new(0);
266 return run_ping_pong_test();
267}
268
269
270TEST_IMPL(tcp_ping_pong_vec) {
271 tcp_pinger_new(1);
272 return run_ping_pong_test();
273}
274
275
276TEST_IMPL(tcp6_ping_pong) {
277 if (!can_ipv6())
278 RETURN_SKIP("IPv6 not supported");
279 tcp_pinger_v6_new(0);
280 return run_ping_pong_test();
281}
282
283
284TEST_IMPL(tcp6_ping_pong_vec) {
285 if (!can_ipv6())
286 RETURN_SKIP("IPv6 not supported");
287 tcp_pinger_v6_new(1);
288 return run_ping_pong_test();
289}
290
291
292TEST_IMPL(pipe_ping_pong) {
293 pipe_pinger_new(0);
294 return run_ping_pong_test();
295}
296
297
298TEST_IMPL(pipe_ping_pong_vec) {
299 pipe_pinger_new(1);
300 return run_ping_pong_test();
301}
302