From 9451220daa74b4cd87e0567e33bb29464c8825df Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 13 Feb 2007 02:52:37 +0000 Subject: Implement ticket #99: a more generic mechanism to implement UAC transaction timeout after provisional response is received git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@942 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsip/sip_transaction.h | 58 ++++++++++++++++++++++++++++++++++ pjsip/src/pjsip/sip_transaction.c | 59 +++++++++++++++++++++++++++++++---- pjsip/src/pjsip/sip_util_statefull.c | 58 +++------------------------------- 3 files changed, 115 insertions(+), 60 deletions(-) diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h index 0b5eee10..5148ca06 100644 --- a/pjsip/include/pjsip/sip_transaction.h +++ b/pjsip/include/pjsip/sip_transaction.h @@ -73,6 +73,21 @@ typedef enum pjsip_tsx_state_e } pjsip_tsx_state_e; +/** + * Transaction timeout timer policy, to control the UAC transaction timeout + * scheduling (see #pjsip_tsx_set_uac_timeout()). + */ +typedef enum pjsip_tsx_timeout_policy +{ + PJSIP_TSX_IGNORE_100 = 1, /**< To make the UAC transaction NOT to cancel + the timeout timer when 100 response is + received.*/ + PJSIP_TSX_IGNORE_1xx = 3 /**< To make the UAC transaction NOT to cancel + the timeout timer when any 1xx responses + are received. */ +} pjsip_tsx_timeout_policy; + + /** * This structure describes SIP transaction object. The transaction object * is used to handle both UAS and UAC transaction. @@ -130,6 +145,9 @@ struct pjsip_transaction pj_timer_entry retransmit_timer;/**< Retransmit timer. */ pj_timer_entry timeout_timer; /**< Timeout timer. */ + unsigned msec_timeout; /**< User set timeout. */ + pjsip_tsx_timeout_policy timeout_policy; /**< Timeout policy. */ + /** Module specific data. */ void *mod_data[PJSIP_MAX_MODULE]; }; @@ -231,6 +249,46 @@ PJ_DECL(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx, const pjsip_tpselector *sel); +/** + * Set the UAC absolute transaction timeout. Normally UAC transaction will + * stop its timeout timer (timer B for INVITE and timer F for non-INVITE + * transactions) after a provisional response is received. + * + * When this timer is set, the transaction's timer B and F will use this + * value, and if the \a flag flag is set, the transaction will continue + * the scheduling of the timeout timer even when provisional response has + * been received. + * + * Note that this function MUST be called AFTER the transaction has been + * created AND BEFORE any request is transmitted. + * + * @param tsx The client/UAC transaction. + * @param msec_time The timeout value, in miliseconds. Currently this + * value must be non-zero (value zero is reserved for + * future use). + * @param flag Option flags to control whether the transaction should + * cancel the timeout timer on arrival of provisional + * responses (which is yes according to RFC 3261). + * The valid values are: + * - PJSIP_TSX_IGNORE_100, to make the UAC transaction + * NOT to cancel the timeout timer when 100 response + * is received. + * - PJSIP_TSX_IGNORE_1xx, to make the UAC transaction + * NOT to cancel the timeout timer when any 1xx + * responses are received. + * + * Note that regardless of the values in the \a flag + * argument, the provisional response would still be + * delivered to transaction user and it will still + * affect the transaction state. The \a flag flag only + * changes the behavior of the timeout timer of the + * transaction. + */ +PJ_DECL(pj_status_t) pjsip_tsx_set_uac_timeout(pjsip_transaction *tsx, + unsigned msec_time, + pjsip_tsx_timeout_policy flag); + + /** * Call this function to manually feed a message to the transaction. * For UAS transaction, application MUST call this function after diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 4dc46520..fcf0646c 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -1395,6 +1395,23 @@ PJ_DEF(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx, } +/* + * Set the UAC absolute transaction timeout. + */ +PJ_DEF(pj_status_t) pjsip_tsx_set_uac_timeout(pjsip_transaction *tsx, + unsigned msec_time, + pjsip_tsx_timeout_policy policy) +{ + PJ_ASSERT_RETURN(tsx && tsx->role==PJSIP_ROLE_UAC && + tsx->state==PJSIP_TSX_STATE_NULL, PJ_EINVALIDOP); + + tsx->msec_timeout = msec_time; + tsx->timeout_policy = policy; + + return PJ_SUCCESS; +} + + /* * Set transaction status code and reason. */ @@ -1896,10 +1913,22 @@ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, } /* Start Timer B (or called timer F for non-INVITE) for transaction - * timeout. + * timeout. If user has configured the timeout value with + * pjsip_tsx_set_uac_timeout(), use the timeout value there. */ - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout_timer_val); + if (tsx->msec_timeout > 0) { + pj_time_val timeout; + + timeout.sec = tsx->msec_timeout / 1000; + timeout.msec = tsx->msec_timeout % 1000; + + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + + } else { + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout_timer_val); + } /* Start Timer A (or timer E) for retransmission only if unreliable * transport is being used. @@ -1967,7 +1996,7 @@ static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, } else if (event->type == PJSIP_EVENT_RX_MSG) { pjsip_msg *msg; - //int code; + int code; /* Get message instance */ msg = event->body.rx_msg.rdata->msg_info.msg; @@ -1984,8 +2013,18 @@ static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED); - /* Cancel timer B (transaction timeout) */ - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); + /* Cancel timer B (transaction timeout) but look at the timeout policy + * as set by pjsip_tsx_set_uac_timeout(). + */ + code = msg->line.status.code; + if ((code==100 && tsx->timeout_policy==PJSIP_TSX_IGNORE_100) || + (code<200 && tsx->timeout_policy==PJSIP_TSX_IGNORE_1xx)) + { + /* Don't cancel the timeout timer */ + } + else { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); + } /* Discard retransmission message if it is not INVITE. * The INVITE tdata is needed in case we have to generate ACK for @@ -2372,6 +2411,14 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); } + } else if (event->type == PJSIP_EVENT_TIMER && + event->body.timer.entry == &tsx->timeout_timer) { + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, &tsx->timeout_timer); + + } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { diff --git a/pjsip/src/pjsip/sip_util_statefull.c b/pjsip/src/pjsip/sip_util_statefull.c index 8056989f..80c4aa74 100644 --- a/pjsip/src/pjsip/sip_util_statefull.c +++ b/pjsip/src/pjsip/sip_util_statefull.c @@ -29,9 +29,6 @@ struct tsx_data { - pj_time_val delay; - pj_timer_entry timeout_timer; - void *token; void (*cb)(void*, pjsip_event*); }; @@ -71,12 +68,6 @@ static void mod_util_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) if (tsx->status_code < 200) return; - /* Cancel timer if any */ - if (tsx_data->timeout_timer.id != 0) { - tsx_data->timeout_timer.id = 0; - pjsip_endpt_cancel_timer(tsx->endpt, &tsx_data->timeout_timer); - } - /* Call the callback, if any, and prevent the callback to be called again * by clearing the transaction's module_data. */ @@ -88,29 +79,6 @@ static void mod_util_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) } -static void mod_util_on_timeout(pj_timer_heap_t *th, pj_timer_entry *te) -{ - pjsip_transaction *tsx = (pjsip_transaction*) te->user_data; - struct tsx_data *tsx_data; - - PJ_UNUSED_ARG(th); - - tsx_data = tsx->mod_data[mod_stateful_util.id]; - if (tsx_data == NULL) { - pj_assert(!"Shouldn't happen"); - return; - } - - tsx_data->timeout_timer.id = 0; - - PJ_LOG(4,(tsx->obj_name, "Transaction timed out by user timer (%d.%d sec)", - (int)tsx_data->delay.sec, (int)tsx_data->delay.msec)); - - /* Terminate the transaction. This will call mod_util_on_tsx_state() */ - pjsip_tsx_terminate(tsx, PJSIP_SC_TSX_TIMEOUT); -} - - PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, pjsip_tx_data *tdata, pj_int32_t timeout, @@ -133,38 +101,20 @@ PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, return status; } - tsx_data = pj_pool_zalloc(tsx->pool, sizeof(struct tsx_data)); + tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct tsx_data)); tsx_data->token = token; tsx_data->cb = cb; if (timeout >= 0) { - tsx_data->delay.sec = 0; - tsx_data->delay.msec = timeout; - pj_time_val_normalize(&tsx_data->delay); - - tsx_data->timeout_timer.id = PJ_TRUE; - tsx_data->timeout_timer.user_data = tsx; - tsx_data->timeout_timer.cb = &mod_util_on_timeout; - - status = pjsip_endpt_schedule_timer(endpt, &tsx_data->timeout_timer, - &tsx_data->delay); - if (status != PJ_SUCCESS) { - pjsip_tsx_terminate(tsx, PJSIP_SC_INTERNAL_SERVER_ERROR); - pjsip_tx_data_dec_ref(tdata); - return status; - } + status = pjsip_tsx_set_uac_timeout(tsx, timeout, PJSIP_TSX_IGNORE_1xx); + pj_assert(status == PJ_SUCCESS); } tsx->mod_data[mod_stateful_util.id] = tsx_data; status = pjsip_tsx_send_msg(tsx, NULL); - if (status != PJ_SUCCESS) { - if (tsx_data->timeout_timer.id != 0) { - pjsip_endpt_cancel_timer(endpt, &tsx_data->timeout_timer); - tsx_data->timeout_timer.id = PJ_FALSE; - } + if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata); - } return status; } -- cgit v1.2.3