blob: 1051b87039e1dd0da495ff3b2d170d2dd98e1ee4 [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
Benny Prijono4df98092008-08-28 10:41:29 +0000321 PJ_UNUSED_ARG(op_key);
322
Benny Prijono417d6052008-07-29 20:15:15 +0000323 st->sent = 1;
324
325 if (sent < 1) {
326 st->err = PJ_TRUE;
327 return PJ_FALSE;
328 }
329
330 return PJ_TRUE;
331}
332
333static int tcp_perf_test(void)
334{
335 enum { COUNT=100000 };
336 pj_pool_t *pool = NULL;
337 pj_ioqueue_t *ioqueue = NULL;
338 pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET;
339 pj_activesock_t *asock1 = NULL, *asock2 = NULL;
340 pj_activesock_cb cb;
341 struct tcp_state *state1, *state2;
342 unsigned i;
343 pj_status_t status;
344
345 pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL);
346
347 status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1,
348 &sock2);
349 if (status != PJ_SUCCESS) {
350 status = -100;
351 goto on_return;
352 }
353
354 status = pj_ioqueue_create(pool, 4, &ioqueue);
355 if (status != PJ_SUCCESS) {
356 status = -110;
357 goto on_return;
358 }
359
360 pj_bzero(&cb, sizeof(cb));
361 cb.on_data_read = &tcp_on_data_read;
362 cb.on_data_sent = &tcp_on_data_sent;
363
364 state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
365 status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue,
366 &cb, state1, &asock1);
367 if (status != PJ_SUCCESS) {
368 status = -120;
369 goto on_return;
370 }
371
372 state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
373 status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue,
374 &cb, state2, &asock2);
375 if (status != PJ_SUCCESS) {
376 status = -130;
377 goto on_return;
378 }
379
380 status = pj_activesock_start_read(asock1, pool, 1000, 0);
381 if (status != PJ_SUCCESS) {
382 status = -140;
383 goto on_return;
384 }
385
386 /* Send packet as quickly as possible */
387 for (i=0; i<COUNT && !state1->err && !state2->err; ++i) {
388 struct tcp_pkt *pkt;
389 struct send_key send_key[2], *op_key;
390 pj_ssize_t len;
391
392 pkt = (struct tcp_pkt*)state2->pkt;
393 pkt->signature = SIGNATURE;
394 pkt->seq = i;
395 pj_memset(pkt->fill, 'a', sizeof(pkt->fill));
396
397 op_key = &send_key[i%2];
398 pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key));
399
400 state2->sent = PJ_FALSE;
401 len = sizeof(*pkt);
402 status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0);
403 if (status == PJ_EPENDING) {
404 do {
405 pj_ioqueue_poll(ioqueue, NULL);
406 } while (!state2->sent);
407 } else if (status != PJ_SUCCESS) {
408 PJ_LOG(1,("", " err: send status=%d", status));
409 status = -180;
410 break;
411 } else if (status == PJ_SUCCESS) {
412 if (len != sizeof(*pkt)) {
413 PJ_LOG(1,("", " err: shouldn't report partial sent"));
414 status = -190;
415 break;
416 }
417 }
418 }
419
420 /* Wait until everything has been sent/received */
421 if (state1->next_recv_seq < COUNT) {
422 pj_time_val delay = {0, 100};
423 while (pj_ioqueue_poll(ioqueue, &delay) > 0)
424 ;
425 }
426
427 if (status == PJ_EPENDING)
428 status = PJ_SUCCESS;
429
430 if (status != 0)
431 goto on_return;
432
433 if (state1->err) {
434 status = -183;
435 goto on_return;
436 }
437 if (state2->err) {
438 status = -186;
439 goto on_return;
440 }
441 if (state1->next_recv_seq != COUNT) {
442 PJ_LOG(3,("", " err: only %u packets received, expecting %u",
443 state1->next_recv_seq, COUNT));
444 status = -195;
445 goto on_return;
446 }
447
448on_return:
449 if (asock2)
450 pj_activesock_close(asock2);
451 if (asock1)
452 pj_activesock_close(asock1);
453 if (ioqueue)
454 pj_ioqueue_destroy(ioqueue);
455 if (pool)
456 pj_pool_release(pool);
457
458 return status;
459}
460
461
462
Benny Prijono4bac2c12008-05-11 18:12:16 +0000463int activesock_test(void)
464{
465 int ret;
466
Benny Prijono417d6052008-07-29 20:15:15 +0000467 ret = (int)&udp_ping_pong_test;
468
Benny Prijono4bac2c12008-05-11 18:12:16 +0000469 PJ_LOG(3,("", "..udp ping/pong test"));
470 ret = udp_ping_pong_test();
471 if (ret != 0)
472 return ret;
473
Benny Prijono417d6052008-07-29 20:15:15 +0000474 PJ_LOG(3,("", "..tcp perf test"));
475 ret = tcp_perf_test();
476 if (ret != 0)
477 return ret;
478
Benny Prijono4bac2c12008-05-11 18:12:16 +0000479 return 0;
480}
481
482#else /* INCLUDE_ACTIVESOCK_TEST */
483/* To prevent warning about "translation unit is empty"
484 * when this test is disabled.
485 */
486int dummy_active_sock_test;
487#endif /* INCLUDE_ACTIVESOCK_TEST */
488