blob: 2eedca0abbb5c96015c80aff59ba6720e9d19c15 [file] [log] [blame]
Benny Prijono4bac2c12008-05-11 18:12:16 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C)2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono4bac2c12008-05-11 18:12:16 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20#include <pjlib.h>
21
22/**
23 * \page page_pjlib_activesock_test Test: Active Socket
24 *
25 * This file is <b>pjlib-test/activesock.c</b>
26 *
27 * \include pjlib-test/activesock.c
28 */
29
30#if INCLUDE_ACTIVESOCK_TEST
31
32#define THIS_FILE "activesock.c"
33
34
35/*******************************************************************
36 * Simple UDP echo server.
37 */
38struct udp_echo_srv
39{
40 pj_activesock_t *asock;
41 pj_bool_t echo_enabled;
42 pj_uint16_t port;
43 pj_ioqueue_op_key_t send_key;
44 pj_status_t status;
45 unsigned rx_cnt;
46 unsigned rx_err_cnt, tx_err_cnt;
47};
48
49static void udp_echo_err(const char *title, pj_status_t status)
50{
51 char errmsg[PJ_ERR_MSG_SIZE];
52
53 pj_strerror(status, errmsg, sizeof(errmsg));
54 PJ_LOG(3,(THIS_FILE, " error: %s: %s", title, errmsg));
55}
56
57static pj_bool_t udp_echo_srv_on_data_recvfrom(pj_activesock_t *asock,
58 void *data,
59 pj_size_t size,
60 const pj_sockaddr_t *src_addr,
61 int addr_len,
62 pj_status_t status)
63{
64 struct udp_echo_srv *srv;
65 pj_ssize_t sent;
66
67
68 srv = (struct udp_echo_srv*) pj_activesock_get_user_data(asock);
69
70 if (status != PJ_SUCCESS) {
71 srv->status = status;
72 srv->rx_err_cnt++;
73 udp_echo_err("recvfrom() callback", status);
74 return PJ_TRUE;
75 }
76
77 srv->rx_cnt++;
78
79 /* Send back if echo is enabled */
80 if (srv->echo_enabled) {
81 sent = size;
82 srv->status = pj_activesock_sendto(asock, &srv->send_key, data,
83 &sent, 0,
84 src_addr, addr_len);
85 if (srv->status != PJ_SUCCESS) {
86 srv->tx_err_cnt++;
87 udp_echo_err("sendto()", status);
88 }
89 }
90
91 return PJ_TRUE;
92}
93
94
95static pj_status_t udp_echo_srv_create(pj_pool_t *pool,
96 pj_ioqueue_t *ioqueue,
97 pj_bool_t enable_echo,
98 struct udp_echo_srv **p_srv)
99{
100 struct udp_echo_srv *srv;
101 pj_sock_t sock_fd = PJ_INVALID_SOCKET;
102 pj_sockaddr addr;
103 int addr_len;
104 pj_activesock_cb activesock_cb;
105 pj_status_t status;
106
107 srv = PJ_POOL_ZALLOC_T(pool, struct udp_echo_srv);
108 srv->echo_enabled = enable_echo;
109
110 pj_sockaddr_in_init(&addr.ipv4, NULL, 0);
111 addr_len = sizeof(addr);
112
113 pj_bzero(&activesock_cb, sizeof(activesock_cb));
114 activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom;
115
116 status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb,
Benny Prijonoea8e4362008-06-06 14:12:23 +0000117 srv, &srv->asock, &addr);
Benny Prijono4bac2c12008-05-11 18:12:16 +0000118 if (status != PJ_SUCCESS) {
119 pj_sock_close(sock_fd);
120 udp_echo_err("pj_activesock_create()", status);
121 return status;
122 }
123
124 srv->port = pj_ntohs(addr.ipv4.sin_port);
125
Benny Prijono4bac2c12008-05-11 18:12:16 +0000126 pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
127
128 status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0);
129 if (status != PJ_SUCCESS) {
130 pj_activesock_close(srv->asock);
131 udp_echo_err("pj_activesock_start_recvfrom()", status);
132 return status;
133 }
134
135
136 *p_srv = srv;
137 return PJ_SUCCESS;
138}
139
140static void udp_echo_srv_destroy(struct udp_echo_srv *srv)
141{
142 pj_activesock_close(srv->asock);
143}
144
145/*******************************************************************
146 * UDP ping pong test (send packet back and forth between two UDP echo
147 * servers.
148 */
149static int udp_ping_pong_test(void)
150{
151 pj_ioqueue_t *ioqueue = NULL;
152 pj_pool_t *pool = NULL;
153 struct udp_echo_srv *srv1=NULL, *srv2=NULL;
154 pj_bool_t need_send = PJ_TRUE;
155 unsigned data = 0;
156 int count, ret;
157 pj_status_t status;
158
159 pool = pj_pool_create(mem, "pingpong", 512, 512, NULL);
160 if (!pool)
161 return -10;
162
163 status = pj_ioqueue_create(pool, 4, &ioqueue);
164 if (status != PJ_SUCCESS) {
165 ret = -20;
166 udp_echo_err("pj_ioqueue_create()", status);
167 goto on_return;
168 }
169
170 status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1);
171 if (status != PJ_SUCCESS) {
172 ret = -30;
173 goto on_return;
174 }
175
176 status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2);
177 if (status != PJ_SUCCESS) {
178 ret = -40;
179 goto on_return;
180 }
181
182 /* initiate the first send */
183 for (count=0; count<1000; ++count) {
184 unsigned last_rx1, last_rx2;
185 unsigned i;
186
187 if (need_send) {
188 pj_str_t loopback;
189 pj_sockaddr_in addr;
190 pj_ssize_t sent;
191
192 ++data;
193
194 sent = sizeof(data);
195 loopback = pj_str("127.0.0.1");
196 pj_sockaddr_in_init(&addr, &loopback, srv2->port);
197 status = pj_activesock_sendto(srv1->asock, &srv1->send_key,
198 &data, &sent, 0,
199 &addr, sizeof(addr));
200 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
201 ret = -50;
202 udp_echo_err("sendto()", status);
203 goto on_return;
204 }
205
206 need_send = PJ_FALSE;
207 }
208
209 last_rx1 = srv1->rx_cnt;
210 last_rx2 = srv2->rx_cnt;
211
212 for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) {
213 pj_time_val delay = {0, 10};
214 pj_ioqueue_poll(ioqueue, &delay);
215 }
216
217 if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 ||
218 srv2->rx_err_cnt+srv2->tx_err_cnt != 0)
219 {
220 /* Got error */
221 ret = -60;
222 goto on_return;
223 }
224
225 if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) {
226 /* Packet lost */
227 ret = -70;
228 udp_echo_err("packets have been lost", PJ_ETIMEDOUT);
229 goto on_return;
230 }
231 }
232
233 ret = 0;
234
235on_return:
236 if (srv2)
237 udp_echo_srv_destroy(srv2);
238 if (srv1)
239 udp_echo_srv_destroy(srv1);
240 if (ioqueue)
241 pj_ioqueue_destroy(ioqueue);
242 if (pool)
243 pj_pool_release(pool);
244
245 return ret;
246}
247
248
Benny Prijono417d6052008-07-29 20:15:15 +0000249
250#define SIGNATURE 0xdeadbeef
251struct tcp_pkt
252{
253 pj_uint32_t signature;
254 pj_uint32_t seq;
255 char fill[513];
256};
257
258struct tcp_state
259{
260 pj_bool_t err;
261 pj_bool_t sent;
262 pj_uint32_t next_recv_seq;
263 pj_uint8_t pkt[600];
264};
265
266struct send_key
267{
268 pj_ioqueue_op_key_t op_key;
269};
270
271
272static pj_bool_t tcp_on_data_read(pj_activesock_t *asock,
273 void *data,
274 pj_size_t size,
275 pj_status_t status,
276 pj_size_t *remainder)
277{
278 struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock);
279 char *next = (char*) data;
280
281 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
282 PJ_LOG(1,("", " err: status=%d", status));
283 st->err = PJ_TRUE;
284 return PJ_FALSE;
285 }
286
287 while (size >= sizeof(struct tcp_pkt)) {
288 struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next;
289
290 if (tcp_pkt->signature != SIGNATURE) {
291 PJ_LOG(1,("", " err: invalid signature at seq=%d",
292 st->next_recv_seq));
293 st->err = PJ_TRUE;
294 return PJ_FALSE;
295 }
296 if (tcp_pkt->seq != st->next_recv_seq) {
297 PJ_LOG(1,("", " err: wrong sequence"));
298 st->err = PJ_TRUE;
299 return PJ_FALSE;
300 }
301
302 st->next_recv_seq++;
303 next += sizeof(struct tcp_pkt);
304 size -= sizeof(struct tcp_pkt);
305 }
306
307 if (size) {
308 pj_memmove(data, next, size);
309 *remainder = size;
310 }
311
312 return PJ_TRUE;
313}
314
315static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock,
316 pj_ioqueue_op_key_t *op_key,
317 pj_ssize_t sent)
318{
319 struct tcp_state *st=(struct tcp_state*)pj_activesock_get_user_data(asock);
320
321 st->sent = 1;
322
323 if (sent < 1) {
324 st->err = PJ_TRUE;
325 return PJ_FALSE;
326 }
327
328 return PJ_TRUE;
329}
330
331static int tcp_perf_test(void)
332{
333 enum { COUNT=100000 };
334 pj_pool_t *pool = NULL;
335 pj_ioqueue_t *ioqueue = NULL;
336 pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET;
337 pj_activesock_t *asock1 = NULL, *asock2 = NULL;
338 pj_activesock_cb cb;
339 struct tcp_state *state1, *state2;
340 unsigned i;
341 pj_status_t status;
342
343 pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL);
344
345 status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1,
346 &sock2);
347 if (status != PJ_SUCCESS) {
348 status = -100;
349 goto on_return;
350 }
351
352 status = pj_ioqueue_create(pool, 4, &ioqueue);
353 if (status != PJ_SUCCESS) {
354 status = -110;
355 goto on_return;
356 }
357
358 pj_bzero(&cb, sizeof(cb));
359 cb.on_data_read = &tcp_on_data_read;
360 cb.on_data_sent = &tcp_on_data_sent;
361
362 state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
363 status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue,
364 &cb, state1, &asock1);
365 if (status != PJ_SUCCESS) {
366 status = -120;
367 goto on_return;
368 }
369
370 state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
371 status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue,
372 &cb, state2, &asock2);
373 if (status != PJ_SUCCESS) {
374 status = -130;
375 goto on_return;
376 }
377
378 status = pj_activesock_start_read(asock1, pool, 1000, 0);
379 if (status != PJ_SUCCESS) {
380 status = -140;
381 goto on_return;
382 }
383
384 /* Send packet as quickly as possible */
385 for (i=0; i<COUNT && !state1->err && !state2->err; ++i) {
386 struct tcp_pkt *pkt;
387 struct send_key send_key[2], *op_key;
388 pj_ssize_t len;
389
390 pkt = (struct tcp_pkt*)state2->pkt;
391 pkt->signature = SIGNATURE;
392 pkt->seq = i;
393 pj_memset(pkt->fill, 'a', sizeof(pkt->fill));
394
395 op_key = &send_key[i%2];
396 pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key));
397
398 state2->sent = PJ_FALSE;
399 len = sizeof(*pkt);
400 status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0);
401 if (status == PJ_EPENDING) {
402 do {
403 pj_ioqueue_poll(ioqueue, NULL);
404 } while (!state2->sent);
405 } else if (status != PJ_SUCCESS) {
406 PJ_LOG(1,("", " err: send status=%d", status));
407 status = -180;
408 break;
409 } else if (status == PJ_SUCCESS) {
410 if (len != sizeof(*pkt)) {
411 PJ_LOG(1,("", " err: shouldn't report partial sent"));
412 status = -190;
413 break;
414 }
415 }
416 }
417
418 /* Wait until everything has been sent/received */
419 if (state1->next_recv_seq < COUNT) {
420 pj_time_val delay = {0, 100};
421 while (pj_ioqueue_poll(ioqueue, &delay) > 0)
422 ;
423 }
424
425 if (status == PJ_EPENDING)
426 status = PJ_SUCCESS;
427
428 if (status != 0)
429 goto on_return;
430
431 if (state1->err) {
432 status = -183;
433 goto on_return;
434 }
435 if (state2->err) {
436 status = -186;
437 goto on_return;
438 }
439 if (state1->next_recv_seq != COUNT) {
440 PJ_LOG(3,("", " err: only %u packets received, expecting %u",
441 state1->next_recv_seq, COUNT));
442 status = -195;
443 goto on_return;
444 }
445
446on_return:
447 if (asock2)
448 pj_activesock_close(asock2);
449 if (asock1)
450 pj_activesock_close(asock1);
451 if (ioqueue)
452 pj_ioqueue_destroy(ioqueue);
453 if (pool)
454 pj_pool_release(pool);
455
456 return status;
457}
458
459
460
Benny Prijono4bac2c12008-05-11 18:12:16 +0000461int activesock_test(void)
462{
463 int ret;
464
Benny Prijono417d6052008-07-29 20:15:15 +0000465 ret = (int)&udp_ping_pong_test;
466
Benny Prijono4bac2c12008-05-11 18:12:16 +0000467 PJ_LOG(3,("", "..udp ping/pong test"));
468 ret = udp_ping_pong_test();
469 if (ret != 0)
470 return ret;
471
Benny Prijono417d6052008-07-29 20:15:15 +0000472 PJ_LOG(3,("", "..tcp perf test"));
473 ret = tcp_perf_test();
474 if (ret != 0)
475 return ret;
476
Benny Prijono4bac2c12008-05-11 18:12:16 +0000477 return 0;
478}
479
480#else /* INCLUDE_ACTIVESOCK_TEST */
481/* To prevent warning about "translation unit is empty"
482 * when this test is disabled.
483 */
484int dummy_active_sock_test;
485#endif /* INCLUDE_ACTIVESOCK_TEST */
486