blob: fc5878252eb6d0e379babd00dcea6eb82b846b19 [file] [log] [blame]
Benny Prijonodbe337a2006-01-08 23:57:52 +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#define THIS_FILE "tsx_uas_test.c"
25
26
27/*****************************************************************************
28 **
29 ** UAS tests.
30 **
31 ** This file performs various tests for UAC transactions. Each test will have
32 ** a different Via branch param so that message receiver module and
33 ** transaction user module can identify which test is being carried out.
34 **
35 ** TEST1_BRANCH_ID
36 ** Test that non-INVITE transaction returns 2xx response to the correct
37 ** transport and correctly terminates the transaction.
38 **
39 ** TEST2_BRANCH_ID
40 ** As above, for non-2xx final response.
41 **
42 ** TEST3_BRANCH_ID
43 ** Transaction correctly progressing to PROCEEDING state when provisional
44 ** response is sent.
45 **
46 ** TEST4_BRANCH_ID
47 ** Transaction retransmits last response (if any) without notifying
48 ** transaction user upon receiving request retransmissions on:
49 ** a. TRYING state.
50 ** a. PROCEEDING state.
51 ** b. COMPLETED state.
52 **
53 ** TEST5_BRANCH_ID
54 ** INVITE transaction MUST retransmit final response. (Note: PJSIP also
55 ** retransmit 2xx final response until it's terminated by user).
56 **
57 ** TEST6_BRANCH_ID
58 ** INVITE transaction MUST cease retransmission of final response when
59 * ACK is received. (Note: PJSIP also retransmit 2xx final response
60 * until it's terminated by user).
61 **
62 ** TEST7_BRANCH_ID
63 ** Test where INVITE UAS transaction never receives ACK
64 **
65 ** TEST8_BRANCH_ID
66 ** When UAS failed to deliver the response with the selected transport,
67 ** it should try contacting the client with other transport or begin
68 ** RFC 3263 server resolution procedure.
69 ** This should be tested on:
70 ** a. TRYING state (when delivering first response).
71 ** b. PROCEEDING state (when failed to retransmit last response
72 ** upon receiving request retransmission).
73 ** c. COMPLETED state.
74 **
75 ** TEST9_BRANCH_ID
76 ** Variant of previous test, where transaction fails to deliver the
77 ** response using any kind of transports. Transaction should report
78 ** transport error to its transaction user.
79 **
80 **/
81
82static char *TEST1_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test1";
83static char *TEST2_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test2";
84static char *TEST3_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test3";
85static char *TEST4_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test4";
86static char *TEST5_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test5";
87static char *TEST6_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test6";
88static char *TEST7_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test7";
89
90#define TEST1_STATUS_CODE 200
91#define TEST2_STATUS_CODE 301
92#define TEST3_PROVISIONAL_CODE PJSIP_SC_QUEUED
93#define TEST3_STATUS_CODE 202
94
95
96static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e);
97static pj_bool_t on_rx_message(pjsip_rx_data *rdata);
98
99/* UAC transaction user module. */
100static pjsip_module tsx_user =
101{
102 NULL, NULL, /* prev and next */
103 { "Tsx-UAS-User", 12}, /* Name. */
104 -1, /* Id */
105 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
106 NULL, /* User data. */
107 0, /* Number of methods supported (=0). */
108 { 0 }, /* Array of methods (none) */
109 NULL, /* load() */
110 NULL, /* start() */
111 NULL, /* stop() */
112 NULL, /* unload() */
113 NULL, /* on_rx_request() */
114 NULL, /* on_rx_response() */
115 NULL, /* on_tx_request() */
116 NULL, /* on_tx_response() */
117 &tsx_user_on_tsx_state, /* on_tsx_state() */
118};
119
120/* Module to send request. */
121static pjsip_module msg_sender =
122{
123 NULL, NULL, /* prev and next */
124 { "Msg-Sender", 10}, /* Name. */
125 -1, /* Id */
126 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
127 NULL, /* User data. */
128 0, /* Number of methods supported (=0). */
129 { 0 }, /* Array of methods (none) */
130 NULL, /* load() */
131 NULL, /* start() */
132 NULL, /* stop() */
133 NULL, /* unload() */
134 &on_rx_message, /* on_rx_request() */
135 &on_rx_message, /* on_rx_response() */
136 NULL, /* on_tx_request() */
137 NULL, /* on_tx_response() */
138 NULL, /* on_tsx_state() */
139};
140
141/* Static vars, which will be reset on each test. */
142static int recv_count;
143static pj_time_val recv_last;
144static pj_bool_t test_complete;
145
146/* Loop transport instance. */
147static pjsip_transport *loop;
148
149/* UAS transaction key. */
150static char key_buf[64];
151static pj_str_t tsx_key = { key_buf, 0 };
152
153
154/* General timer entry to be used by tests. */
155static pj_timer_entry timer;
156
157/* Timer to send response via transaction. */
158struct response
159{
160 pj_str_t tsx_key;
161 pjsip_tx_data *tdata;
162};
163
164static void send_response_timer( pj_timer_heap_t *timer_heap,
165 struct pj_timer_entry *entry)
166{
167 pjsip_transaction *tsx;
168 struct response *r = entry->user_data;
169 pj_status_t status;
170
171 tsx = pjsip_tsx_layer_find_tsx(&r->tsx_key, PJ_TRUE);
172 if (!tsx) {
173 PJ_LOG(3,(THIS_FILE," error: timer unable to find transaction"));
174 pjsip_tx_data_dec_ref(r->tdata);
175 return;
176 }
177
178 status = pjsip_tsx_send_msg(tsx, r->tdata);
179 if (status != PJ_SUCCESS) {
180 PJ_LOG(3,(THIS_FILE," error: timer unable to send response"));
181 pjsip_tx_data_dec_ref(r->tdata);
182 return;
183 }
184}
185
186/* Schedule timer to send response for the specified UAS transaction */
187static void schedule_send_response( pjsip_rx_data *rdata,
188 const pj_str_t *tsx_key,
189 int status_code,
190 int msec_delay )
191{
192 pj_status_t status;
193 pjsip_tx_data *tdata;
194 struct response *r;
195 pj_time_val delay;
196
197 status = pjsip_endpt_create_response( endpt, rdata, status_code, NULL,
198 &tdata);
199 if (status != PJ_SUCCESS) {
200 app_perror(" error: unable to create response", status);
201 test_complete = -198;
202 return;
203 }
204
205 r = pj_pool_alloc(tdata->pool, sizeof(*r));
206 pj_strdup(tdata->pool, &r->tsx_key, tsx_key);
207 r->tdata = tdata;
208
209 delay.sec = 0;
210 delay.msec = msec_delay;
211 pj_time_val_normalize(&delay);
212
213 timer.user_data = r;
214 timer.cb = &send_response_timer;
215
216 status = pjsip_endpt_schedule_timer(endpt, &timer, &delay);
217 if (status != PJ_SUCCESS) {
218 app_perror(" error: unable to schedule timer", status);
219 test_complete = -199;
220 pjsip_tx_data_dec_ref(tdata);
221 return;
222 }
223}
224
225/*
226 * This is the handler to receive state changed notification from the
227 * transaction. It is used to verify that the transaction behaves according
228 * to the test scenario.
229 */
230static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
231{
232 if (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0 ||
233 pj_strcmp2(&tsx->branch, TEST2_BRANCH_ID)==0)
234 {
235 /*
236 * TEST1_BRANCH_ID tests that non-INVITE transaction transmits final
237 * response using correct transport and terminates transaction after
238 * T4 (PJSIP_T4_TIMEOUT, 5 seconds).
239 *
240 * TEST2_BRANCH_ID does similar test for non-2xx final response.
241 */
242 int status_code = (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ?
243 TEST1_STATUS_CODE : TEST2_STATUS_CODE;
244
245 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
246
247 test_complete = 1;
248
249 /* Check that status code is status_code. */
250 if (tsx->status_code != status_code) {
251 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
252 test_complete = -100;
253 }
254
255 /* Previous state must be completed. */
256 if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
257 PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
258 test_complete = -101;
259 }
260
261 } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
262
263 /* Previous state must be TRYING. */
264 if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
265 PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
266 test_complete = -102;
267 }
268 }
269
270 }
271 else
272 if (pj_strcmp2(&tsx->branch, TEST3_BRANCH_ID)==0) {
273 /*
274 * TEST3_BRANCH_ID tests sending provisional response.
275 */
276 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
277
278 test_complete = 1;
279
280 /* Check that status code is status_code. */
281 if (tsx->status_code != TEST3_STATUS_CODE) {
282 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
283 test_complete = -110;
284 }
285
286 /* Previous state must be completed. */
287 if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
288 PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
289 test_complete = -111;
290 }
291
292 } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) {
293
294 /* Previous state must be TRYING. */
295 if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
296 PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
297 test_complete = -112;
298 }
299
300 /* Check that status code is status_code. */
301 if (tsx->status_code != TEST3_PROVISIONAL_CODE) {
302 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
303 test_complete = -113;
304 }
305
306 /* Check that event must be TX_MSG */
307 if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) {
308 PJ_LOG(3,(THIS_FILE, " error: incorrect event"));
309 test_complete = -114;
310 }
311
312 } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
313
314 /* Previous state must be PROCEEDING. */
315 if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) {
316 PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
317 test_complete = -115;
318 }
319
320 /* Check that status code is status_code. */
321 if (tsx->status_code != TEST3_STATUS_CODE) {
322 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
323 test_complete = -116;
324 }
325
326 /* Check that event must be TX_MSG */
327 if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) {
328 PJ_LOG(3,(THIS_FILE, " error: incorrect event"));
329 test_complete = -117;
330 }
331
332 }
333
334 }
335
336}
337
338/* Save transaction key to global variables. */
339static void save_key(pjsip_transaction *tsx)
340{
341 pj_str_t key;
342
343 pj_strdup(tsx->pool, &key, &tsx->transaction_key);
344 pj_strcpy(&tsx_key, &key);
345}
346
347/*
348 * Message receiver handler.
349 */
350static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
351{
352 pjsip_msg *msg = rdata->msg_info.msg;
353 pj_str_t branch_param = rdata->msg_info.via->branch_param;
354 pj_status_t status;
355
356 if (pj_strcmp2(&branch_param, TEST1_BRANCH_ID) == 0 ||
357 pj_strcmp2(&branch_param, TEST2_BRANCH_ID) == 0)
358 {
359 /*
360 * TEST1_BRANCH_ID tests that non-INVITE transaction transmits 2xx
361 * final response using correct transport and terminates transaction
362 * after 32 seconds.
363 *
364 * TEST2_BRANCH_ID performs similar test for non-2xx final response.
365 */
366 int status_code = (pj_strcmp2(&branch_param, TEST1_BRANCH_ID) == 0) ?
367 TEST1_STATUS_CODE : TEST2_STATUS_CODE;
368
369 if (msg->type == PJSIP_REQUEST_MSG) {
370 /* On received response, create UAS and respond with final
371 * response.
372 */
373 pjsip_transaction *tsx;
374 pjsip_tx_data *tdata;
375
376 status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
377 if (status != PJ_SUCCESS) {
378 app_perror(" error: unable to create transaction", status);
379 test_complete = -110;
380 return PJ_TRUE;
381 }
382
383 save_key(tsx);
384
385 status = pjsip_endpt_create_response(endpt, rdata,
386 status_code, NULL,
387 &tdata);
388 if (status != PJ_SUCCESS) {
389 app_perror(" error: unable to create response", status);
390 test_complete = -111;
391 return PJ_TRUE;
392 }
393
394 status = pjsip_tsx_send_msg(tsx, tdata);
395 if (status != PJ_SUCCESS) {
396 app_perror(" error: unable to send response", status);
397 test_complete = -112;
398 return PJ_TRUE;
399 }
400
401 } else {
402 /* Verify the response received. */
403
404 ++recv_count;
405
406 /* Verify status code. */
407 if (msg->line.status.code != status_code) {
408 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
409 test_complete = -113;
410 }
411
412 /* Verify that no retransmissions is received. */
413 if (recv_count > 1) {
414 PJ_LOG(3,(THIS_FILE, " error: retransmission received"));
415 test_complete = -114;
416 }
417
418 }
419 return PJ_TRUE;
420
421 } else if (pj_strcmp2(&branch_param, TEST3_BRANCH_ID) == 0) {
422
423 /* TEST3_BRANCH_ID tests provisional response. */
424
425 if (msg->type == PJSIP_REQUEST_MSG) {
426 /* On received response, create UAS and respond with provisional
427 * response, then schedule timer to send final response.
428 */
429 pjsip_transaction *tsx;
430 pjsip_tx_data *tdata;
431
432 status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
433 if (status != PJ_SUCCESS) {
434 app_perror(" error: unable to create transaction", status);
435 test_complete = -120;
436 return PJ_TRUE;
437 }
438
439 save_key(tsx);
440
441 status = pjsip_endpt_create_response(endpt, rdata,
442 TEST3_PROVISIONAL_CODE, NULL,
443 &tdata);
444 if (status != PJ_SUCCESS) {
445 app_perror(" error: unable to create response", status);
446 test_complete = -121;
447 return PJ_TRUE;
448 }
449
450 status = pjsip_tsx_send_msg(tsx, tdata);
451 if (status != PJ_SUCCESS) {
452 app_perror(" error: unable to send response", status);
453 test_complete = -122;
454 return PJ_TRUE;
455 }
456
457 schedule_send_response(rdata, &tsx->transaction_key,
458 TEST3_STATUS_CODE, 2000);
459
460 } else {
461 /* Verify the response received. */
462
463 ++recv_count;
464
465 if (recv_count == 1) {
466 /* Verify status code. */
467 if (msg->line.status.code != TEST3_PROVISIONAL_CODE) {
468 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
469 test_complete = -123;
470 }
471 } else if (recv_count == 2) {
472 /* Verify status code. */
473 if (msg->line.status.code != TEST3_STATUS_CODE) {
474 PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
475 test_complete = -124;
476 }
477 } else {
478 PJ_LOG(3,(THIS_FILE, " error: retransmission received"));
479 test_complete = -125;
480 }
481
482 }
483 return PJ_TRUE;
484
485 }
486
487 return PJ_FALSE;
488}
489
490/*
491 * The generic test framework, used by most of the tests.
492 */
493static int perform_test( char *target_uri, char *from_uri,
494 char *branch_param, int test_time,
495 const pjsip_method *method )
496{
497 pjsip_tx_data *tdata;
498 pj_str_t target, from;
499 pjsip_via_hdr *via;
500 pj_time_val timeout;
501 pj_status_t status;
502
503 PJ_LOG(3,(THIS_FILE,
504 " please standby, this will take at most %d seconds..",
505 test_time));
506
507 /* Reset test. */
508 recv_count = 0;
509 test_complete = 0;
510 tsx_key.slen = 0;
511
512 /* Init headers. */
513 target = pj_str(target_uri);
514 from = pj_str(from_uri);
515
516 /* Create request. */
517 status = pjsip_endpt_create_request( endpt, method, &target,
518 &from, &target, NULL, NULL, -1,
519 NULL, &tdata);
520 if (status != PJ_SUCCESS) {
521 app_perror(" Error: unable to create request", status);
522 return -10;
523 }
524
525 /* Set the branch param for test 1. */
526 via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
527 via->branch_param = pj_str(branch_param);
528
529 /* Add additional reference to tdata to prevent transaction from
530 * deleting it.
531 */
532 pjsip_tx_data_add_ref(tdata);
533
534 /* Send the first message. */
535 status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL);
536 if (status != PJ_SUCCESS) {
537 app_perror(" Error: unable to send request", status);
538 return -20;
539 }
540
541 /* Set test completion time. */
542 pj_gettimeofday(&timeout);
543 timeout.sec += test_time;
544
545 /* Wait until test complete. */
546 while (!test_complete) {
547 pj_time_val now, poll_delay = {0, 10};
548
549 pjsip_endpt_handle_events(endpt, &poll_delay);
550
551 pj_gettimeofday(&now);
552 if (now.sec > timeout.sec) {
553 PJ_LOG(3,(THIS_FILE, " Error: test has timed out"));
554 pjsip_tx_data_dec_ref(tdata);
555 return -30;
556 }
557 }
558
559 if (test_complete < 0) {
560 pjsip_transaction *tsx;
561
562 tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
563 if (tsx) {
564 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED);
565 pj_mutex_unlock(tsx->mutex);
566 flush_events(1000);
567 }
568 pjsip_tx_data_dec_ref(tdata);
569 return test_complete;
570 }
571
572 /* Allow transaction to destroy itself */
573 flush_events(500);
574
575 /* Make sure transaction has been destroyed. */
576 if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) {
577 PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed"));
578 pjsip_tx_data_dec_ref(tdata);
579 return -40;
580 }
581
582 /* Check tdata reference counter. */
583 if (pj_atomic_get(tdata->ref_cnt) != 1) {
584 PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %d",
585 pj_atomic_get(tdata->ref_cnt)));
586 pjsip_tx_data_dec_ref(tdata);
587 return -50;
588 }
589
590 /* Destroy txdata */
591 pjsip_tx_data_dec_ref(tdata);
592
593 return PJ_SUCCESS;
594
595}
596
597
598/*****************************************************************************
599 **
600 ** TEST1_BRANCH_ID: Basic 2xx final response
601 ** TEST2_BRANCH_ID: Basic non-2xx final response
602 **
603 *****************************************************************************
604 */
605static int tsx_basic_final_response_test(void)
606{
607 int status;
608
609 PJ_LOG(3,(THIS_FILE," test1: basic sending 2xx final response"));
610
611 status = perform_test("sip:129.0.0.1;transport=loop-dgram",
612 "sip:129.0.0.1;transport=loop-dgram",
613 TEST1_BRANCH_ID,
614 33, /* Test duration must be greater than 32 secs */
615 &pjsip_options_method);
616 if (status != 0)
617 return status;
618
619 PJ_LOG(3,(THIS_FILE," test2: basic sending non-2xx final response"));
620
621 status = perform_test("sip:129.0.0.1;transport=loop-dgram",
622 "sip:129.0.0.1;transport=loop-dgram",
623 TEST2_BRANCH_ID,
624 33, /* Test duration must be greater than 32 secs */
625 &pjsip_options_method);
626 if (status != 0)
627 return status;
628
629 return 0;
630}
631
632
633/*****************************************************************************
634 **
635 ** TEST3_BRANCH_ID: Sending provisional response
636 **
637 *****************************************************************************
638 */
639static int tsx_basic_provisional_response_test(void)
640{
641 int status;
642
643 PJ_LOG(3,(THIS_FILE," test1: basic sending 2xx final response"));
644
645 status = perform_test("sip:129.0.0.1;transport=loop-dgram",
646 "sip:129.0.0.1;transport=loop-dgram",
647 TEST3_BRANCH_ID,
648 35,
649 &pjsip_options_method);
650 return status;
651}
652
653
654/*****************************************************************************
655 **
656 ** UAS Transaction Test.
657 **
658 *****************************************************************************
659 */
660int tsx_uas_test(void)
661{
662 pj_sockaddr_in addr;
663 pj_status_t status;
664
665 /* Check if loop transport is configured. */
666 status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM,
667 &addr, sizeof(addr), &loop);
668 if (status != PJ_SUCCESS) {
669 PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!"));
670 return -1;
671 }
672
673 /* Register modules. */
674 status = pjsip_endpt_register_module(endpt, &tsx_user);
675 if (status != PJ_SUCCESS) {
676 app_perror(" Error: unable to register module", status);
677 return -3;
678 }
679 status = pjsip_endpt_register_module(endpt, &msg_sender);
680 if (status != PJ_SUCCESS) {
681 app_perror(" Error: unable to register module", status);
682 return -4;
683 }
684
685#if 0
686 /* TEST1_BRANCH_ID: Basic 2xx final response.
687 * TEST2_BRANCH_ID: Basic non-2xx final response.
688 */
689 status = tsx_basic_final_response_test();
690 if (status != 0)
691 return status;
692#endif
693
694 /* TEST3_BRANCH_ID: with provisional response
695 */
696 status = tsx_basic_provisional_response_test();
697 if (status != 0)
698 return status;
699
700
701 pjsip_transport_dec_ref(loop);
702 return 0;
703
704}
705