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