blob: 560f0c665dfc290dc5a49469a9a0a993e6de20eb [file] [log] [blame]
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001/* $Id$ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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 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
112 pj_bzero(&activesock_cb, sizeof(activesock_cb));
113 activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom;
114
115 status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb,
116 srv, &srv->asock, &addr);
117 if (status != PJ_SUCCESS) {
118 pj_sock_close(sock_fd);
119 udp_echo_err("pj_activesock_create()", status);
120 return status;
121 }
122
123 srv->port = pj_ntohs(addr.ipv4.sin_port);
124
125 pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
126
127 status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0);
128 if (status != PJ_SUCCESS) {
129 pj_activesock_close(srv->asock);
130 udp_echo_err("pj_activesock_start_recvfrom()", status);
131 return status;
132 }
133
134
135 *p_srv = srv;
136 return PJ_SUCCESS;
137}
138
139static void udp_echo_srv_destroy(struct udp_echo_srv *srv)
140{
141 pj_activesock_close(srv->asock);
142}
143
144/*******************************************************************
145 * UDP ping pong test (send packet back and forth between two UDP echo
146 * servers.
147 */
148static int udp_ping_pong_test(void)
149{
150 pj_ioqueue_t *ioqueue = NULL;
151 pj_pool_t *pool = NULL;
152 struct udp_echo_srv *srv1=NULL, *srv2=NULL;
153 pj_bool_t need_send = PJ_TRUE;
154 unsigned data = 0;
155 int count, ret;
156 pj_status_t status;
157
158 pool = pj_pool_create(mem, "pingpong", 512, 512, NULL);
159 if (!pool)
160 return -10;
161
162 status = pj_ioqueue_create(pool, 4, &ioqueue);
163 if (status != PJ_SUCCESS) {
164 ret = -20;
165 udp_echo_err("pj_ioqueue_create()", status);
166 goto on_return;
167 }
168
169 status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1);
170 if (status != PJ_SUCCESS) {
171 ret = -30;
172 goto on_return;
173 }
174
175 status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2);
176 if (status != PJ_SUCCESS) {
177 ret = -40;
178 goto on_return;
179 }
180
181 /* initiate the first send */
182 for (count=0; count<1000; ++count) {
183 unsigned last_rx1, last_rx2;
184 unsigned i;
185
186 if (need_send) {
187 pj_str_t loopback;
188 pj_sockaddr_in addr;
189 pj_ssize_t sent;
190
191 ++data;
192
193 sent = sizeof(data);
194 loopback = pj_str("127.0.0.1");
195 pj_sockaddr_in_init(&addr, &loopback, srv2->port);
196 status = pj_activesock_sendto(srv1->asock, &srv1->send_key,
197 &data, &sent, 0,
198 &addr, sizeof(addr));
199 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
200 ret = -50;
201 udp_echo_err("sendto()", status);
202 goto on_return;
203 }
204
205 need_send = PJ_FALSE;
206 }
207
208 last_rx1 = srv1->rx_cnt;
209 last_rx2 = srv2->rx_cnt;
210
211 for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) {
212 pj_time_val delay = {0, 10};
213#ifdef PJ_SYMBIAN
214 PJ_UNUSED_ARG(delay);
215 pj_symbianos_poll(-1, 100);
216#else
217 pj_ioqueue_poll(ioqueue, &delay);
218#endif
219 }
220
221 if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 ||
222 srv2->rx_err_cnt+srv2->tx_err_cnt != 0)
223 {
224 /* Got error */
225 ret = -60;
226 goto on_return;
227 }
228
229 if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) {
230 /* Packet lost */
231 ret = -70;
232 udp_echo_err("packets have been lost", PJ_ETIMEDOUT);
233 goto on_return;
234 }
235 }
236
237 ret = 0;
238
239on_return:
240 if (srv2)
241 udp_echo_srv_destroy(srv2);
242 if (srv1)
243 udp_echo_srv_destroy(srv1);
244 if (ioqueue)
245 pj_ioqueue_destroy(ioqueue);
246 if (pool)
247 pj_pool_release(pool);
248
249 return ret;
250}
251
252
253
254#define SIGNATURE 0xdeadbeef
255struct tcp_pkt
256{
257 pj_uint32_t signature;
258 pj_uint32_t seq;
259 char fill[513];
260};
261
262struct tcp_state
263{
264 pj_bool_t err;
265 pj_bool_t sent;
266 pj_uint32_t next_recv_seq;
267 pj_uint8_t pkt[600];
268};
269
270struct send_key
271{
272 pj_ioqueue_op_key_t op_key;
273};
274
275
276static pj_bool_t tcp_on_data_read(pj_activesock_t *asock,
277 void *data,
278 pj_size_t size,
279 pj_status_t status,
280 pj_size_t *remainder)
281{
282 struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock);
283 char *next = (char*) data;
284
285 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
286 PJ_LOG(1,("", " err: status=%d", status));
287 st->err = PJ_TRUE;
288 return PJ_FALSE;
289 }
290
291 while (size >= sizeof(struct tcp_pkt)) {
292 struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next;
293
294 if (tcp_pkt->signature != SIGNATURE) {
295 PJ_LOG(1,("", " err: invalid signature at seq=%d",
296 st->next_recv_seq));
297 st->err = PJ_TRUE;
298 return PJ_FALSE;
299 }
300 if (tcp_pkt->seq != st->next_recv_seq) {
301 PJ_LOG(1,("", " err: wrong sequence"));
302 st->err = PJ_TRUE;
303 return PJ_FALSE;
304 }
305
306 st->next_recv_seq++;
307 next += sizeof(struct tcp_pkt);
308 size -= sizeof(struct tcp_pkt);
309 }
310
311 if (size) {
312 pj_memmove(data, next, size);
313 *remainder = size;
314 }
315
316 return PJ_TRUE;
317}
318
319static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock,
320 pj_ioqueue_op_key_t *op_key,
321 pj_ssize_t sent)
322{
323 struct tcp_state *st=(struct tcp_state*)pj_activesock_get_user_data(asock);
324
325 PJ_UNUSED_ARG(op_key);
326
327 st->sent = 1;
328
329 if (sent < 1) {
330 st->err = PJ_TRUE;
331 return PJ_FALSE;
332 }
333
334 return PJ_TRUE;
335}
336
337static int tcp_perf_test(void)
338{
339 enum { COUNT=10000 };
340 pj_pool_t *pool = NULL;
341 pj_ioqueue_t *ioqueue = NULL;
342 pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET;
343 pj_activesock_t *asock1 = NULL, *asock2 = NULL;
344 pj_activesock_cb cb;
345 struct tcp_state *state1, *state2;
346 unsigned i;
347 pj_status_t status;
348
349 pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL);
350
351 status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1,
352 &sock2);
353 if (status != PJ_SUCCESS) {
354 status = -100;
355 goto on_return;
356 }
357
358 status = pj_ioqueue_create(pool, 4, &ioqueue);
359 if (status != PJ_SUCCESS) {
360 status = -110;
361 goto on_return;
362 }
363
364 pj_bzero(&cb, sizeof(cb));
365 cb.on_data_read = &tcp_on_data_read;
366 cb.on_data_sent = &tcp_on_data_sent;
367
368 state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
369 status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue,
370 &cb, state1, &asock1);
371 if (status != PJ_SUCCESS) {
372 status = -120;
373 goto on_return;
374 }
375
376 state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
377 status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue,
378 &cb, state2, &asock2);
379 if (status != PJ_SUCCESS) {
380 status = -130;
381 goto on_return;
382 }
383
384 status = pj_activesock_start_read(asock1, pool, 1000, 0);
385 if (status != PJ_SUCCESS) {
386 status = -140;
387 goto on_return;
388 }
389
390 /* Send packet as quickly as possible */
391 for (i=0; i<COUNT && !state1->err && !state2->err; ++i) {
392 struct tcp_pkt *pkt;
393 struct send_key send_key[2], *op_key;
394 pj_ssize_t len;
395
396 pkt = (struct tcp_pkt*)state2->pkt;
397 pkt->signature = SIGNATURE;
398 pkt->seq = i;
399 pj_memset(pkt->fill, 'a', sizeof(pkt->fill));
400
401 op_key = &send_key[i%2];
402 pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key));
403
404 state2->sent = PJ_FALSE;
405 len = sizeof(*pkt);
406 status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0);
407 if (status == PJ_EPENDING) {
408 do {
409#if PJ_SYMBIAN
410 pj_symbianos_poll(-1, -1);
411#else
412 pj_ioqueue_poll(ioqueue, NULL);
413#endif
414 } while (!state2->sent);
415 } else {
416#if PJ_SYMBIAN
417 /* The Symbian socket always returns PJ_SUCCESS for TCP send,
418 * eventhough the remote end hasn't received the data yet.
419 * If we continue sending, eventually send() will block,
420 * possibly because the send buffer is full. So we need to
421 * poll the ioqueue periodically, to let receiver gets the
422 * data.
423 */
424 pj_symbianos_poll(-1, 0);
425#endif
426 if (status != PJ_SUCCESS) {
427 PJ_LOG(1,("", " err: send status=%d", status));
428 status = -180;
429 break;
430 } else if (status == PJ_SUCCESS) {
431 if (len != sizeof(*pkt)) {
432 PJ_LOG(1,("", " err: shouldn't report partial sent"));
433 status = -190;
434 break;
435 }
436 }
437 }
438
439#ifndef PJ_SYMBIAN
440 for (;;) {
441 pj_time_val timeout = {0, 10};
442 if (pj_ioqueue_poll(ioqueue, &timeout) < 1)
443 break;
444 }
445#endif
446
447 }
448
449 /* Wait until everything has been sent/received */
450 if (state1->next_recv_seq < COUNT) {
451#ifdef PJ_SYMBIAN
452 while (pj_symbianos_poll(-1, 1000) == PJ_TRUE)
453 ;
454#else
455 pj_time_val delay = {0, 100};
456 while (pj_ioqueue_poll(ioqueue, &delay) > 0)
457 ;
458#endif
459 }
460
461 if (status == PJ_EPENDING)
462 status = PJ_SUCCESS;
463
464 if (status != 0)
465 goto on_return;
466
467 if (state1->err) {
468 status = -183;
469 goto on_return;
470 }
471 if (state2->err) {
472 status = -186;
473 goto on_return;
474 }
475 if (state1->next_recv_seq != COUNT) {
476 PJ_LOG(3,("", " err: only %u packets received, expecting %u",
477 state1->next_recv_seq, COUNT));
478 status = -195;
479 goto on_return;
480 }
481
482on_return:
483 if (asock2)
484 pj_activesock_close(asock2);
485 if (asock1)
486 pj_activesock_close(asock1);
487 if (ioqueue)
488 pj_ioqueue_destroy(ioqueue);
489 if (pool)
490 pj_pool_release(pool);
491
492 return status;
493}
494
495
496
497int activesock_test(void)
498{
499 int ret;
500
501 PJ_LOG(3,("", "..udp ping/pong test"));
502 ret = udp_ping_pong_test();
503 if (ret != 0)
504 return ret;
505
506 PJ_LOG(3,("", "..tcp perf test"));
507 ret = tcp_perf_test();
508 if (ret != 0)
509 return ret;
510
511 return 0;
512}
513
514#else /* INCLUDE_ACTIVESOCK_TEST */
515/* To prevent warning about "translation unit is empty"
516 * when this test is disabled.
517 */
518int dummy_active_sock_test;
519#endif /* INCLUDE_ACTIVESOCK_TEST */
520