blob: 76b6bb9e30cc811a158d54dc77fbffc1a61e2ba4 [file] [log] [blame]
Benny Prijono0ca04b62005-12-30 23:50:15 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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
20#include "test.h"
21#include <pjsip_core.h>
22#include <pjlib.h>
23
24///////////////////////////////////////////////////////////////////////////////
25/*
26 * Generic testing for transport, to make sure that basic
27 * attributes have been initialized properly.
28 */
29int generic_transport_test(pjsip_transport *tp)
30{
31 PJ_LOG(3,("", " structure test..."));
32
33 /* Check that local address name is valid. */
34 {
35 struct pj_in_addr addr;
36
37 /* Note: inet_aton() returns non-zero if addr is valid! */
38 if (pj_inet_aton(&tp->local_name.host, &addr) != 0) {
39 if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) {
40 PJ_LOG(3,("", " Error: invalid address name"));
41 return -420;
42 }
43 } else {
44 /* It's okay. local_name.host may be a hostname instead of
45 * IP address.
46 */
47 }
48 }
49
50 /* Check that port is valid. */
51 if (tp->local_name.port <= 0) {
52 return -430;
53 }
54
55 /* Check length of address (for now we only check against sockaddr_in). */
56 if (tp->addr_len != sizeof(pj_sockaddr_in))
57 return -440;
58
59 /* Check type. */
60 if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED)
61 return -450;
62
63 /* That's it. */
64 return PJ_SUCCESS;
65}
66
67///////////////////////////////////////////////////////////////////////////////
68/*
69 * Send/receive test.
70 *
71 * This test sends a request to loopback address; as soon as request is
72 * received, response will be sent, and time is recorded.
73 *
74 * The main purpose is to test that the basic transport functionalities works,
75 * before we continue with more complicated tests.
76 */
77#define FROM_HDR "Bob <sip:bob@example.com>"
Benny Prijono0ca04b62005-12-30 23:50:15 +000078#define CONTACT_HDR "Bob <sip:bob@127.0.0.1>"
79#define CALL_ID_HDR "SendRecv-Test"
80#define CSEQ_VALUE 100
81#define BODY "Hello World!"
82
83static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata);
84static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata);
85
86/* Flag to indicate message has been received
87 * (or failed to send)
88 */
89#define NO_STATUS -2
90static int send_status = NO_STATUS;
91static int recv_status = NO_STATUS;
92static pj_timestamp my_send_time, my_recv_time;
93
94/* Module to receive messages for this test. */
95static pjsip_module my_module =
96{
97 NULL, NULL, /* prev and next */
98 { "Transport-Test", 14}, /* Name. */
99 -1, /* Id */
100 PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
101 NULL, /* User data. */
102 0, /* Number of methods supported (=0). */
103 { 0 }, /* Array of methods (none) */
104 NULL, /* load() */
105 NULL, /* start() */
106 NULL, /* stop() */
107 NULL, /* unload() */
108 &my_on_rx_request, /* on_rx_request() */
109 &my_on_rx_response, /* on_rx_response() */
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000110 NULL, /* on_tsx_state() */
Benny Prijono0ca04b62005-12-30 23:50:15 +0000111};
112
113
114static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
115{
116 /* Check that this is our request. */
117 if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
118 /* It is! */
119 /* Send response. */
120 pjsip_tx_data *tdata;
121 pjsip_response_addr res_addr;
122 pj_status_t status;
123
124 PJ_LOG(4,("test", "Received %d bytes request: --begin-\n"
125 "%s\n"
126 "--end--",
127 rdata->msg_info.len,
128 rdata->msg_info.msg_buf));
129
130
131 status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
132 if (status != PJ_SUCCESS) {
133 recv_status = status;
134 return PJ_TRUE;
135 }
136 status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
137 if (status != PJ_SUCCESS) {
138 recv_status = status;
139 pjsip_tx_data_dec_ref(tdata);
140 return PJ_TRUE;
141 }
142 status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
143 if (status != PJ_SUCCESS) {
144 recv_status = status;
145 pjsip_tx_data_dec_ref(tdata);
146 return PJ_TRUE;
147 }
148 return PJ_TRUE;
149 }
150
151 /* Not ours. */
152 return PJ_FALSE;
153}
154
155static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
156{
157 if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
158 PJ_LOG(4,("test", "Received %d bytes response: --begin-\n"
159 "%s\n"
160 "--end--",
161 rdata->msg_info.len,
162 rdata->msg_info.msg_buf));
163
164 pj_get_timestamp(&my_recv_time);
165 recv_status = PJ_SUCCESS;
166 return PJ_TRUE;
167 }
168 return PJ_FALSE;
169}
170
171/* Transport callback. */
172static void send_msg_callback(pjsip_send_state *stateless_data,
173 pj_ssize_t sent, pj_bool_t *cont)
174{
175 if (sent < 1) {
176 /* Obtain the error code. */
177 send_status = -sent;
178 } else {
179 send_status = PJ_SUCCESS;
180 }
181
182 /* Don't want to continue. */
183 *cont = PJ_FALSE;
184}
185
186
187/* Test that we receive loopback message. */
188int transport_send_recv_test( pjsip_transport_type_e tp_type,
189 pjsip_transport *ref_tp,
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000190 char *target_url )
Benny Prijono0ca04b62005-12-30 23:50:15 +0000191{
192 pj_status_t status;
Benny Prijono0ca04b62005-12-30 23:50:15 +0000193 pj_str_t target, from, to, contact, call_id, body;
194 pjsip_method method;
195 pjsip_tx_data *tdata;
196 pj_time_val timeout;
197
198 PJ_LOG(3,("", " single message round-trip test..."));
199
200 /* Register out test module to receive the message (if necessary). */
201 if (my_module.id == -1) {
202 status = pjsip_endpt_register_module( endpt, &my_module );
203 if (status != PJ_SUCCESS) {
204 app_perror(" error: unable to register module", status);
205 return -500;
206 }
207 }
208
209 /* Create a request message. */
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000210 target = pj_str(target_url);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000211 from = pj_str(FROM_HDR);
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000212 to = pj_str(target_url);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000213 contact = pj_str(CONTACT_HDR);
214 call_id = pj_str(CALL_ID_HDR);
215 body = pj_str(BODY);
216
217 pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
218 status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
219 &contact, &call_id, CSEQ_VALUE,
220 &body, &tdata );
221 if (status != PJ_SUCCESS) {
222 app_perror(" error: unable to create request", status);
223 return -510;
224 }
225
226 /* Reset statuses */
227 send_status = recv_status = NO_STATUS;
228
229 /* Start time. */
230 pj_get_timestamp(&my_send_time);
231
232 /* Send the message (statelessly). */
233 status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
234 &send_msg_callback);
235 if (status != PJ_SUCCESS) {
236 /* Immediate error! */
237 pjsip_tx_data_dec_ref(tdata);
238 send_status = status;
239 }
240
241 /* Set the timeout (1 second from now) */
242 pj_gettimeofday(&timeout);
243 timeout.sec += 1;
244
245 /* Loop handling events until we get status */
246 do {
247 pj_time_val now;
248 pj_time_val poll_interval = { 0, 10 };
249
250 pj_gettimeofday(&now);
251 if (PJ_TIME_VAL_GTE(now, timeout)) {
252 PJ_LOG(3,("", " error: timeout in send/recv test"));
253 status = -540;
254 goto on_return;
255 }
256
257 if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) {
258 app_perror(" error sending message", send_status);
259 status = -550;
260 goto on_return;
261 }
262
263 if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) {
264 app_perror(" error receiving message", recv_status);
265 status = -560;
266 goto on_return;
267 }
268
269 if (send_status!=NO_STATUS && recv_status!=NO_STATUS) {
270 /* Success! */
271 break;
272 }
273
274 pjsip_endpt_handle_events(endpt, &poll_interval);
275
276 } while (1);
277
278 if (status == PJ_SUCCESS) {
279 unsigned usec_rt;
280 usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
281 PJ_LOG(3,("", " round-trip = %d usec", usec_rt));
282 }
283
284 status = PJ_SUCCESS;
285
286on_return:
287 return status;
288}
289
290
291///////////////////////////////////////////////////////////////////////////////
292/*
293 * Multithreaded round-trip test
294 *
295 * This test will spawn multiple threads, each of them send a request. As soon
296 * as request is received, response will be sent, and time is recorded.
297 *
298 * The main purpose of this test is to ensure there's no crash when multiple
299 * threads are sending/receiving messages.
300 *
301 */
302static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata);
303static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata);
304
305static pjsip_module rt_module =
306{
307 NULL, NULL, /* prev and next */
308 { "Transport-RT-Test", 17}, /* Name. */
309 -1, /* Id */
310 PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
311 NULL, /* User data. */
312 0, /* Number of methods supported (=0). */
313 { 0 }, /* Array of methods (none) */
314 NULL, /* load() */
315 NULL, /* start() */
316 NULL, /* stop() */
317 NULL, /* unload() */
318 &rt_on_rx_request, /* on_rx_request() */
319 &rt_on_rx_response, /* on_rx_response() */
320 NULL, /* tsx_handler() */
321};
322
323static struct
324{
325 pj_thread_t *thread;
326 pj_timestamp send_time;
327 pj_timestamp total_rt_time;
328 int sent_request_count, recv_response_count;
329 pj_str_t call_id;
330} rt_test_data[16];
331
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000332static char rt_target_uri[64];
Benny Prijono0ca04b62005-12-30 23:50:15 +0000333static pj_bool_t rt_stop;
334static pj_str_t rt_call_id;
335
336static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
337{
338 if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
339 char *pos = pj_strchr(&rdata->msg_info.call_id, '/');
340 int thread_id = (*pos - '0');
341
342 pjsip_tx_data *tdata;
343 pjsip_response_addr res_addr;
344 pj_status_t status;
345
346 status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
347 if (status != PJ_SUCCESS) {
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000348 app_perror(" error creating response", status);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000349 return PJ_TRUE;
350 }
351 status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
352 if (status != PJ_SUCCESS) {
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000353 app_perror(" error in get response address", status);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000354 pjsip_tx_data_dec_ref(tdata);
355 return PJ_TRUE;
356 }
357 status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
358 if (status != PJ_SUCCESS) {
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000359 app_perror(" error sending response", status);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000360 pjsip_tx_data_dec_ref(tdata);
361 return PJ_TRUE;
362 }
363 return PJ_TRUE;
364
365 }
366 return PJ_FALSE;
367}
368
369static pj_status_t rt_send_request(int thread_id)
370{
371 pj_status_t status;
372 pj_str_t target, from, to, contact, call_id;
373 pjsip_tx_data *tdata;
374
375 /* Create a request message. */
376 target = pj_str(rt_target_uri);
377 from = pj_str(FROM_HDR);
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000378 to = pj_str(rt_target_uri);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000379 contact = pj_str(CONTACT_HDR);
380 call_id = rt_test_data[thread_id].call_id;
381
382 status = pjsip_endpt_create_request( endpt, &pjsip_options_method,
383 &target, &from, &to,
384 &contact, &call_id, -1,
385 NULL, &tdata );
386 if (status != PJ_SUCCESS) {
387 app_perror(" error: unable to create request", status);
388 return -610;
389 }
390
391 /* Start time. */
392 pj_get_timestamp(&rt_test_data[thread_id].send_time);
393
394 /* Send the message (statelessly). */
395 status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL);
396 if (status != PJ_SUCCESS) {
397 /* Immediate error! */
398 app_perror(" error: send request", status);
399 pjsip_tx_data_dec_ref(tdata);
400 return -620;
401 }
402
403 /* Update counter. */
404 rt_test_data[thread_id].sent_request_count++;
405
406 return PJ_SUCCESS;
407}
408
409static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
410{
411 if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
412 char *pos = pj_strchr(&rdata->msg_info.call_id, '/')+1;
413 int thread_id = (*pos - '0');
414 pj_timestamp recv_time;
415
416 /* Update counter and end-time. */
417 rt_test_data[thread_id].recv_response_count++;
418 pj_get_timestamp(&recv_time);
419
420 pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time);
421 pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time);
422
423 if (!rt_stop)
424 rt_send_request(thread_id);
425 return PJ_TRUE;
426 }
427 return PJ_FALSE;
428}
429
430static int rt_thread(void *arg)
431{
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000432 int i, thread_id = (int)arg;
Benny Prijono0ca04b62005-12-30 23:50:15 +0000433 pj_time_val poll_delay = { 0, 10 };
434
435 /* Sleep to allow main threads to run. */
436 pj_thread_sleep(10);
437
438 /* Send the first request. */
439 if (rt_send_request(thread_id) != PJ_SUCCESS)
440 return -1;
441
442 while (!rt_stop) {
443 pjsip_endpt_handle_events(endpt, &poll_delay);
444 }
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000445
446 /* Exhaust responses. */
447 for (i=0; i<100; ++i)
448 pjsip_endpt_handle_events(endpt, &poll_delay);
449
Benny Prijono0ca04b62005-12-30 23:50:15 +0000450 return 0;
451}
452
453int transport_rt_test( pjsip_transport_type_e tp_type,
454 pjsip_transport *ref_tp,
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000455 char *target_url )
Benny Prijono0ca04b62005-12-30 23:50:15 +0000456{
457 enum { THREADS = 4, INTERVAL = 10 };
458 int i;
459 pj_status_t status;
460 pj_pool_t *pool;
461 pj_bool_t is_reliable;
462
463 pj_timestamp zero_time, total_time;
464 unsigned usec_rt;
465 unsigned total_sent;
466 unsigned total_recv;
467
468
469 PJ_LOG(3,("", " multithreaded round-trip test (%d threads)...",
470 THREADS));
471 PJ_LOG(3,("", " this will take approx %d seconds, please wait..", INTERVAL));
472
473 is_reliable = (pjsip_transport_get_flag_from_type(tp_type) & PJSIP_TRANSPORT_RELIABLE);
474
475 /* Register module (if not yet registered) */
476 if (rt_module.id == -1) {
477 status = pjsip_endpt_register_module( endpt, &rt_module );
478 if (status != PJ_SUCCESS) {
479 app_perror(" error: unable to register module", status);
480 return -600;
481 }
482 }
483
484 /* Create pool for this test. */
485 pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
486 if (!pool)
487 return -610;
488
489 /* Initialize static test data. */
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000490 pj_native_strcpy(rt_target_uri, target_url);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000491 rt_call_id = pj_str("RT-Call-Id/");
492 rt_stop = PJ_FALSE;
493
494 /* Initialize thread data. */
495 for (i=0; i<THREADS; ++i) {
496 char buf[1];
497 pj_str_t str_id = { buf, 1 };
498
499 pj_memset(&rt_test_data[i], 0, sizeof(rt_test_data[i]));
500
501 /* Generate Call-ID for each thread. */
502 rt_test_data[i].call_id.ptr = pj_pool_alloc(pool, rt_call_id.slen+1);
503 pj_strcpy(&rt_test_data[i].call_id, &rt_call_id);
504 buf[0] = '0' + i;
505 pj_strcat(&rt_test_data[i].call_id, &str_id);
506
507 /* Create thread, suspended. */
508 status = pj_thread_create(pool, "rttest%p", &rt_thread, (void*)i, 0,
509 PJ_THREAD_SUSPENDED, &rt_test_data[i].thread);
510 if (status != PJ_SUCCESS) {
511 app_perror(" error: unable to create thread", status);
512 return -620;
513 }
514 }
515
516 /* Start threads! */
517 for (i=0; i<THREADS; ++i) {
518 pj_thread_resume(rt_test_data[i].thread);
519 }
520
521 /* Sleep for some time. */
522 pj_thread_sleep(INTERVAL * 1000);
523
524 /* Signal thread to stop. */
525 rt_stop = PJ_TRUE;
526
527 /* Wait threads to complete. */
528 for (i=0; i<THREADS; ++i) {
529 pj_thread_join(rt_test_data[i].thread);
530 pj_thread_destroy(rt_test_data[i].thread);
531 }
532
533 /* Gather statistics. */
534 pj_memset(&total_time, 0, sizeof(total_time));
535 pj_memset(&zero_time, 0, sizeof(zero_time));
536 usec_rt = total_sent = total_recv = 0;
537 for (i=0; i<THREADS; ++i) {
538 total_sent += rt_test_data[i].sent_request_count;
539 total_recv += rt_test_data[i].recv_response_count;
540 pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time);
541 }
542
543 /* Display statistics. */
544 if (total_recv)
545 total_time.u64 = total_time.u64/total_recv;
546 else
547 total_time.u64 = 0;
548 usec_rt = pj_elapsed_usec(&zero_time, &total_time);
549 PJ_LOG(3,("", " done."));
550 PJ_LOG(3,("", " total %d messages sent", total_sent));
551 if (total_sent-total_recv)
552 PJ_LOG(2,("", " total %d messages LOST", total_sent-total_recv));
553 else
554 PJ_LOG(3,("", " no message was lost"));
555 PJ_LOG(3,("", " average round-trip=%d usec", usec_rt));
556
Benny Prijonofa73e3e2006-01-05 23:35:46 +0000557 pjsip_endpt_release_pool(endpt, pool);
Benny Prijono0ca04b62005-12-30 23:50:15 +0000558
559 if (is_reliable && (total_sent != total_recv)) {
560 PJ_LOG(3,("", " error: %d messages lost", total_sent-total_recv));
561 return -650;
562 }
563 return 0;
564}