1
/* $Id: transport_test.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
25
#define THIS_FILE "transport_test.c"
27
///////////////////////////////////////////////////////////////////////////////
29
* Generic testing for transport, to make sure that basic
30
* attributes have been initialized properly.
32
int generic_transport_test(pjsip_transport *tp)
34
PJ_LOG(3,(THIS_FILE, " structure test..."));
36
/* Check that local address name is valid. */
38
struct pj_in_addr addr;
40
/* Note: inet_aton() returns non-zero if addr is valid! */
41
if (pj_inet_aton(&tp->local_name.host, &addr) != 0) {
42
if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) {
43
PJ_LOG(3,(THIS_FILE, " Error: invalid address name"));
47
/* It's okay. local_name.host may be a hostname instead of
53
/* Check that port is valid. */
54
if (tp->local_name.port <= 0) {
58
/* Check length of address (for now we only check against sockaddr_in). */
59
if (tp->addr_len != sizeof(pj_sockaddr_in))
63
if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED)
70
///////////////////////////////////////////////////////////////////////////////
74
* This test sends a request to loopback address; as soon as request is
75
* received, response will be sent, and time is recorded.
77
* The main purpose is to test that the basic transport functionalities works,
78
* before we continue with more complicated tests.
80
#define FROM_HDR "Bob <sip:bob@example.com>"
81
#define CONTACT_HDR "Bob <sip:bob@127.0.0.1>"
82
#define CALL_ID_HDR "SendRecv-Test"
83
#define CSEQ_VALUE 100
84
#define BODY "Hello World!"
86
static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata);
87
static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata);
89
/* Flag to indicate message has been received
93
static int send_status = NO_STATUS;
94
static int recv_status = NO_STATUS;
95
static pj_timestamp my_send_time, my_recv_time;
97
/* Module to receive messages for this test. */
98
static pjsip_module my_module =
100
NULL, NULL, /* prev and next */
101
{ "Transport-Test", 14}, /* Name. */
103
PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
108
&my_on_rx_request, /* on_rx_request() */
109
&my_on_rx_response, /* on_rx_response() */
110
NULL, /* on_tsx_state() */
114
static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
116
/* Check that this is our request. */
117
if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
120
pjsip_tx_data *tdata;
121
pjsip_response_addr res_addr;
124
status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
125
if (status != PJ_SUCCESS) {
126
recv_status = status;
129
status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
130
if (status != PJ_SUCCESS) {
131
recv_status = status;
132
pjsip_tx_data_dec_ref(tdata);
135
status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
136
if (status != PJ_SUCCESS) {
137
recv_status = status;
138
pjsip_tx_data_dec_ref(tdata);
148
static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
150
if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
151
pj_get_timestamp(&my_recv_time);
152
recv_status = PJ_SUCCESS;
158
/* Transport callback. */
159
static void send_msg_callback(pjsip_send_state *stateless_data,
160
pj_ssize_t sent, pj_bool_t *cont)
162
PJ_UNUSED_ARG(stateless_data);
165
/* Obtain the error code. */
168
send_status = PJ_SUCCESS;
171
/* Don't want to continue. */
176
/* Test that we receive loopback message. */
177
int transport_send_recv_test( pjsip_transport_type_e tp_type,
178
pjsip_transport *ref_tp,
182
pj_bool_t msg_log_enabled;
184
pj_str_t target, from, to, contact, call_id, body;
186
pjsip_tx_data *tdata;
189
PJ_UNUSED_ARG(tp_type);
190
PJ_UNUSED_ARG(ref_tp);
192
PJ_LOG(3,(THIS_FILE, " single message round-trip test..."));
194
/* Register out test module to receive the message (if necessary). */
195
if (my_module.id == -1) {
196
status = pjsip_endpt_register_module( endpt, &my_module );
197
if (status != PJ_SUCCESS) {
198
app_perror(" error: unable to register module", status);
203
/* Disable message logging. */
204
msg_log_enabled = msg_logger_set_enabled(0);
206
/* Create a request message. */
207
target = pj_str(target_url);
208
from = pj_str(FROM_HDR);
209
to = pj_str(target_url);
210
contact = pj_str(CONTACT_HDR);
211
call_id = pj_str(CALL_ID_HDR);
214
pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
215
status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
216
&contact, &call_id, CSEQ_VALUE,
218
if (status != PJ_SUCCESS) {
219
app_perror(" error: unable to create request", status);
224
send_status = recv_status = NO_STATUS;
227
pj_get_timestamp(&my_send_time);
229
/* Send the message (statelessly). */
230
PJ_LOG(5,(THIS_FILE, "Sending request to %.*s",
231
(int)target.slen, target.ptr));
232
status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
234
if (status != PJ_SUCCESS) {
235
/* Immediate error! */
236
pjsip_tx_data_dec_ref(tdata);
237
send_status = status;
240
/* Set the timeout (2 seconds from now) */
241
pj_gettimeofday(&timeout);
244
/* Loop handling events until we get status */
247
pj_time_val poll_interval = { 0, 10 };
249
pj_gettimeofday(&now);
250
if (PJ_TIME_VAL_GTE(now, timeout)) {
251
PJ_LOG(3,(THIS_FILE, " error: timeout in send/recv test"));
256
if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) {
257
app_perror(" error sending message", send_status);
262
if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) {
263
app_perror(" error receiving message", recv_status);
268
if (send_status!=NO_STATUS && recv_status!=NO_STATUS) {
273
pjsip_endpt_handle_events(endpt, &poll_interval);
277
if (status == PJ_SUCCESS) {
279
usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
281
PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt));
283
*p_usec_rtt = usec_rt;
286
/* Restore message logging. */
287
msg_logger_set_enabled(msg_log_enabled);
296
///////////////////////////////////////////////////////////////////////////////
298
* Multithreaded round-trip test
300
* This test will spawn multiple threads, each of them send a request. As soon
301
* as request is received, response will be sent, and time is recorded.
303
* The main purpose of this test is to ensure there's no crash when multiple
304
* threads are sending/receiving messages.
307
static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata);
308
static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata);
310
static pjsip_module rt_module =
312
NULL, NULL, /* prev and next */
313
{ "Transport-RT-Test", 17}, /* Name. */
315
PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
320
&rt_on_rx_request, /* on_rx_request() */
321
&rt_on_rx_response, /* on_rx_response() */
322
NULL, /* tsx_handler() */
328
pj_timestamp send_time;
329
pj_timestamp total_rt_time;
330
int sent_request_count, recv_response_count;
332
pj_timer_entry timeout_timer;
333
pj_timer_entry tx_timer;
337
static char rt_target_uri[64];
338
static pj_bool_t rt_stop;
339
static pj_str_t rt_call_id;
341
static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
343
if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
344
pjsip_tx_data *tdata;
345
pjsip_response_addr res_addr;
348
status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
349
if (status != PJ_SUCCESS) {
350
app_perror(" error creating response", status);
353
status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
354
if (status != PJ_SUCCESS) {
355
app_perror(" error in get response address", status);
356
pjsip_tx_data_dec_ref(tdata);
359
status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
360
if (status != PJ_SUCCESS) {
361
app_perror(" error sending response", status);
362
pjsip_tx_data_dec_ref(tdata);
371
static pj_status_t rt_send_request(int thread_id)
374
pj_str_t target, from, to, contact, call_id;
375
pjsip_tx_data *tdata;
376
pj_time_val timeout_delay;
378
pj_mutex_lock(rt_test_data[thread_id].mutex);
380
/* Create a request message. */
381
target = pj_str(rt_target_uri);
382
from = pj_str(FROM_HDR);
383
to = pj_str(rt_target_uri);
384
contact = pj_str(CONTACT_HDR);
385
call_id = rt_test_data[thread_id].call_id;
387
status = pjsip_endpt_create_request( endpt, &pjsip_options_method,
389
&contact, &call_id, -1,
391
if (status != PJ_SUCCESS) {
392
app_perror(" error: unable to create request", status);
393
pj_mutex_unlock(rt_test_data[thread_id].mutex);
398
pj_get_timestamp(&rt_test_data[thread_id].send_time);
400
/* Send the message (statelessly). */
401
status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL);
402
if (status != PJ_SUCCESS) {
403
/* Immediate error! */
404
app_perror(" error: send request", status);
405
pjsip_tx_data_dec_ref(tdata);
406
pj_mutex_unlock(rt_test_data[thread_id].mutex);
410
/* Update counter. */
411
rt_test_data[thread_id].sent_request_count++;
413
/* Set timeout timer. */
414
if (rt_test_data[thread_id].timeout_timer.user_data != NULL) {
415
pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer);
417
timeout_delay.sec = 100; timeout_delay.msec = 0;
418
rt_test_data[thread_id].timeout_timer.user_data = (void*)1;
419
pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].timeout_timer,
422
pj_mutex_unlock(rt_test_data[thread_id].mutex);
426
static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
428
if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
429
char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1;
430
int thread_id = (*pos - '0');
431
pj_timestamp recv_time;
433
pj_mutex_lock(rt_test_data[thread_id].mutex);
436
pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer);
438
/* Update counter and end-time. */
439
rt_test_data[thread_id].recv_response_count++;
440
pj_get_timestamp(&recv_time);
442
pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time);
443
pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time);
446
pj_time_val tx_delay = { 0, 0 };
447
pj_assert(rt_test_data[thread_id].tx_timer.user_data == NULL);
448
rt_test_data[thread_id].tx_timer.user_data = (void*)1;
449
pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].tx_timer,
453
pj_mutex_unlock(rt_test_data[thread_id].mutex);
460
static void rt_timeout_timer( pj_timer_heap_t *timer_heap,
461
struct pj_timer_entry *entry )
463
pj_mutex_lock(rt_test_data[entry->id].mutex);
465
PJ_UNUSED_ARG(timer_heap);
466
PJ_LOG(3,(THIS_FILE, " timeout waiting for response"));
467
rt_test_data[entry->id].timeout_timer.user_data = NULL;
469
if (rt_test_data[entry->id].tx_timer.user_data == NULL) {
470
pj_time_val delay = { 0, 0 };
471
rt_test_data[entry->id].tx_timer.user_data = (void*)1;
472
pjsip_endpt_schedule_timer(endpt, &rt_test_data[entry->id].tx_timer,
476
pj_mutex_unlock(rt_test_data[entry->id].mutex);
479
static void rt_tx_timer( pj_timer_heap_t *timer_heap,
480
struct pj_timer_entry *entry )
482
pj_mutex_lock(rt_test_data[entry->id].mutex);
484
PJ_UNUSED_ARG(timer_heap);
485
pj_assert(rt_test_data[entry->id].tx_timer.user_data != NULL);
486
rt_test_data[entry->id].tx_timer.user_data = NULL;
487
rt_send_request(entry->id);
489
pj_mutex_unlock(rt_test_data[entry->id].mutex);
493
static int rt_worker_thread(void *arg)
496
pj_time_val poll_delay = { 0, 10 };
500
/* Sleep to allow main threads to run. */
504
pjsip_endpt_handle_events(endpt, &poll_delay);
507
/* Exhaust responses. */
508
for (i=0; i<100; ++i)
509
pjsip_endpt_handle_events(endpt, &poll_delay);
514
int transport_rt_test( pjsip_transport_type_e tp_type,
515
pjsip_transport *ref_tp,
519
enum { THREADS = 4, INTERVAL = 10 };
523
pj_bool_t logger_enabled;
525
pj_timestamp zero_time, total_time;
530
PJ_UNUSED_ARG(tp_type);
531
PJ_UNUSED_ARG(ref_tp);
533
PJ_LOG(3,(THIS_FILE, " multithreaded round-trip test (%d threads)...",
535
PJ_LOG(3,(THIS_FILE, " this will take approx %d seconds, please wait..",
538
/* Make sure msg logger is disabled. */
539
logger_enabled = msg_logger_set_enabled(0);
541
/* Register module (if not yet registered) */
542
if (rt_module.id == -1) {
543
status = pjsip_endpt_register_module( endpt, &rt_module );
544
if (status != PJ_SUCCESS) {
545
app_perror(" error: unable to register module", status);
550
/* Create pool for this test. */
551
pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
555
/* Initialize static test data. */
556
pj_ansi_strcpy(rt_target_uri, target_url);
557
rt_call_id = pj_str("RT-Call-Id/");
560
/* Initialize thread data. */
561
for (i=0; i<THREADS; ++i) {
565
pj_strset(&str_id, buf, 1);
566
pj_bzero(&rt_test_data[i], sizeof(rt_test_data[i]));
568
/* Init timer entry */
569
rt_test_data[i].tx_timer.id = i;
570
rt_test_data[i].tx_timer.cb = &rt_tx_timer;
571
rt_test_data[i].timeout_timer.id = i;
572
rt_test_data[i].timeout_timer.cb = &rt_timeout_timer;
574
/* Generate Call-ID for each thread. */
575
rt_test_data[i].call_id.ptr = (char*) pj_pool_alloc(pool, rt_call_id.slen+1);
576
pj_strcpy(&rt_test_data[i].call_id, &rt_call_id);
577
buf[0] = '0' + (char)i;
578
pj_strcat(&rt_test_data[i].call_id, &str_id);
581
status = pj_mutex_create_recursive(pool, "rt", &rt_test_data[i].mutex);
582
if (status != PJ_SUCCESS) {
583
app_perror(" error: unable to create mutex", status);
587
/* Create thread, suspended. */
588
status = pj_thread_create(pool, "rttest%p", &rt_worker_thread, (void*)(long)i, 0,
589
PJ_THREAD_SUSPENDED, &rt_test_data[i].thread);
590
if (status != PJ_SUCCESS) {
591
app_perror(" error: unable to create thread", status);
597
for (i=0; i<THREADS; ++i) {
598
pj_time_val delay = {0,0};
599
pj_thread_resume(rt_test_data[i].thread);
601
/* Schedule first message transmissions. */
602
rt_test_data[i].tx_timer.user_data = (void*)1;
603
pjsip_endpt_schedule_timer(endpt, &rt_test_data[i].tx_timer, &delay);
606
/* Sleep for some time. */
607
pj_thread_sleep(INTERVAL * 1000);
609
/* Signal thread to stop. */
612
/* Wait threads to complete. */
613
for (i=0; i<THREADS; ++i) {
614
pj_thread_join(rt_test_data[i].thread);
615
pj_thread_destroy(rt_test_data[i].thread);
618
/* Destroy rt_test_data */
619
for (i=0; i<THREADS; ++i) {
620
pj_mutex_destroy(rt_test_data[i].mutex);
621
pjsip_endpt_cancel_timer(endpt, &rt_test_data[i].timeout_timer);
624
/* Gather statistics. */
625
pj_bzero(&total_time, sizeof(total_time));
626
pj_bzero(&zero_time, sizeof(zero_time));
627
usec_rt = total_sent = total_recv = 0;
628
for (i=0; i<THREADS; ++i) {
629
total_sent += rt_test_data[i].sent_request_count;
630
total_recv += rt_test_data[i].recv_response_count;
631
pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time);
634
/* Display statistics. */
636
total_time.u64 = total_time.u64/total_recv;
639
usec_rt = pj_elapsed_usec(&zero_time, &total_time);
640
PJ_LOG(3,(THIS_FILE, " done."));
641
PJ_LOG(3,(THIS_FILE, " total %d messages sent", total_sent));
642
PJ_LOG(3,(THIS_FILE, " average round-trip=%d usec", usec_rt));
644
pjsip_endpt_release_pool(endpt, pool);
646
*lost = total_sent-total_recv;
651
/* Restore msg logger. */
652
msg_logger_set_enabled(logger_enabled);
657
///////////////////////////////////////////////////////////////////////////////
659
* Transport load testing
661
static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata);
663
static struct mod_load_test
671
NULL, NULL, /* prev and next */
672
{ "mod-load-test", 13}, /* Name. */
674
PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
679
&load_on_rx_request, /* on_rx_request() */
680
NULL, /* on_rx_response() */
681
NULL, /* tsx_handler() */
686
static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata)
688
if (rdata->msg_info.cseq->cseq != mod_load.next_seq) {
689
PJ_LOG(1,("THIS_FILE", " err: expecting cseq %u, got %u",
690
mod_load.next_seq, rdata->msg_info.cseq->cseq));
691
mod_load.err = PJ_TRUE;
692
mod_load.next_seq = rdata->msg_info.cseq->cseq + 1;
698
int transport_load_test(char *target_url)
700
enum { COUNT = 2000 };
702
pj_status_t status = PJ_SUCCESS;
704
/* exhaust packets */
706
pj_time_val delay = {1, 0};
708
pjsip_endpt_handle_events2(endpt, &delay, &i);
711
PJ_LOG(3,(THIS_FILE, " transport load test..."));
713
if (mod_load.mod.id == -1) {
714
status = pjsip_endpt_register_module( endpt, &mod_load.mod);
715
if (status != PJ_SUCCESS) {
716
app_perror("error registering module", status);
720
mod_load.err = PJ_FALSE;
721
mod_load.next_seq = 0;
723
for (i=0; i<COUNT && !mod_load.err; ++i) {
724
pj_str_t target, from, call_id;
725
pjsip_tx_data *tdata;
727
target = pj_str(target_url);
728
from = pj_str("<sip:user@host>");
729
call_id = pj_str("thecallid");
730
status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
732
&target, &from, &call_id,
734
if (status != PJ_SUCCESS) {
735
app_perror("error creating request", status);
739
status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL);
740
if (status != PJ_SUCCESS) {
741
app_perror("error sending request", status);
747
pj_time_val delay = {1, 0};
749
pjsip_endpt_handle_events2(endpt, &delay, &i);
752
if (mod_load.next_seq != COUNT) {
753
PJ_LOG(1,("THIS_FILE", " err: expecting %u msg, got only %u",
754
COUNT, mod_load.next_seq));
760
if (mod_load.mod.id != -1) {
761
pjsip_endpt_unregister_module( endpt, &mod_load.mod);
762
mod_load.mod.id = -1;
764
if (status != PJ_SUCCESS || mod_load.err) {
767
PJ_LOG(3,(THIS_FILE, " success"));