diff options
author | Benny Prijono <bennylp@teluu.com> | 2010-06-21 13:28:55 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2010-06-21 13:28:55 +0000 |
commit | 9a61cdc66183f1c4ed0c4a4e27ebc1b6a67ac183 (patch) | |
tree | 939edcabf9094e583729718acfe0087bf238c8c3 /pjsip/src/pjsip-ua | |
parent | 6fa4d964245209d05d9c1cce6e8f12ab993a03a1 (diff) |
Fixes #1047 (Don't send UPDATE if remote doesn't support it (thanks Bogdan Krakowski for the report)) and fixes #1097 (Support sending UPDATE without SDP). Details:
- Session timer fixes:
- will look at remote capability in Allow header
- if UPDATE is supported, will send UPDATE without SDP first.
If this fails, will send UPDATE with SDP
- otherwise will send re-INVITE
- PJSUA-LIB will look at dialog's remote capability to determine
whether re-INVITE or UPDATE should be sent to change default
addresses after ICE negotiation.
- pjsip_inv_update() now allows NULL offer, in which case the
UPDATE will be sent without SDP.
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3215 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src/pjsip-ua')
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 52 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_timer.c | 98 |
2 files changed, 101 insertions, 49 deletions
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index ecfebec3..c58a92f7 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -2393,7 +2393,7 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, pj_status_t status = PJ_SUCCESS; /* Verify arguments. */ - PJ_ASSERT_RETURN(inv && p_tdata && offer, PJ_EINVAL); + PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* Dialog must have been established */ PJ_ASSERT_RETURN(inv->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED, @@ -2406,24 +2406,26 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); - /* Process offer */ - if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) { - PJ_LOG(4,(inv->dlg->obj_name, - "Invalid SDP offer/answer state for UPDATE")); - status = PJ_EINVALIDOP; - goto on_error; - } + /* Process offer, if any */ + if (offer) { + if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) { + PJ_LOG(4,(inv->dlg->obj_name, + "Invalid SDP offer/answer state for UPDATE")); + status = PJ_EINVALIDOP; + goto on_error; + } - /* Notify negotiator about the new offer. This will fix the offer - * with correct SDP origin. - */ - status = pjmedia_sdp_neg_modify_local_offer(inv->pool_prov, inv->neg, - offer); - if (status != PJ_SUCCESS) - goto on_error; + /* Notify negotiator about the new offer. This will fix the offer + * with correct SDP origin. + */ + status = pjmedia_sdp_neg_modify_local_offer(inv->pool_prov, inv->neg, + offer); + if (status != PJ_SUCCESS) + goto on_error; - /* Retrieve the "fixed" offer from negotiator */ - pjmedia_sdp_neg_get_neg_local(inv->neg, &offer); + /* Retrieve the "fixed" offer from negotiator */ + pjmedia_sdp_neg_get_neg_local(inv->neg, &offer); + } /* Update Contact if required */ if (new_contact) { @@ -2449,8 +2451,10 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, goto on_error; /* Attach SDP body */ - sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer); - pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body); + if (offer) { + sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer); + pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body); + } /* Unlock dialog. */ pjsip_dlg_dec_lock(inv->dlg); @@ -2879,6 +2883,16 @@ static void inv_handle_update_response( pjsip_inv_session *inv, /* Get/attach invite session's transaction data */ else { + /* Session-Timer needs to see any error responses, to determine + * whether peer supports UPDATE with empty body. + */ + if (tsx->state == PJSIP_TSX_STATE_COMPLETED && + tsx->role == PJSIP_ROLE_UAC) + { + status = handle_timer_response(inv, e->body.tsx_state.src.rdata, + PJ_FALSE); + } + tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; if (tsx_inv_data == NULL) { tsx_inv_data=PJ_POOL_ZALLOC_T(tsx->pool, struct tsx_inv_data); diff --git a/pjsip/src/pjsip-ua/sip_timer.c b/pjsip/src/pjsip-ua/sip_timer.c index 34676e0d..bb97c3d0 100644 --- a/pjsip/src/pjsip-ua/sip_timer.c +++ b/pjsip/src/pjsip-ua/sip_timer.c @@ -59,6 +59,7 @@ struct pjsip_timer pj_timer_entry timer; /**< Timer entry */ pj_bool_t use_update; /**< Use UPDATE method to refresh the session */ + pj_bool_t with_sdp; /**< SDP in UPDATE? */ pjsip_role_e role; /**< Role in last INVITE/ UPDATE transaction. */ @@ -321,7 +322,7 @@ static void add_timer_headers(pjsip_inv_session *inv, pjsip_tx_data *tdata, * the session if UA is the refresher, otherwise it is time to end * the session. */ -void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) +static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data; pjsip_tx_data *tdata = NULL; @@ -330,38 +331,55 @@ void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) pj_assert(inv); - PJ_UNUSED_ARG(timer_heap); - - /* When there is a pending INVITE transaction, delay/reschedule this timer - * for five seconds to cover the case that pending INVITE fails and the - * previous session is still active. If the pending INVITE is successful, - * timer state will be updated, i.e: restarted or stopped. - */ - if (inv->invite_tsx != NULL) { - pj_time_val delay = {5}; + inv->timer->timer.id = 0; - inv->timer->timer.id = 1; - pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer, &delay); - return; - } + PJ_UNUSED_ARG(timer_heap); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Check our role */ - as_refresher = + as_refresher = (inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) || (inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS); /* Do action based on role, refresher or refreshee */ if (as_refresher) { - pj_time_val now; + /* As refresher, reshedule the refresh request on the following: + * - msut not send re-INVITE if another INVITE or SDP negotiation + * is in progress. + * - must not send UPDATE with SDP if SDP negotiation is in progress + */ + pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg); + if ( (!inv->timer->use_update && ( + inv->invite_tsx != NULL || + neg_state != PJMEDIA_SDP_NEG_STATE_DONE) + ) + || + (inv->timer->use_update && inv->timer->with_sdp && + neg_state != PJMEDIA_SDP_NEG_STATE_DONE + ) + ) + { + pj_time_val delay = {1, 0}; + + inv->timer->timer.id = 1; + pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer, + &delay); + pjsip_dlg_dec_lock(inv->dlg); + return; + } + /* Refresher, refresh the session */ if (inv->timer->use_update) { - /* Create UPDATE request without offer */ - status = pjsip_inv_update(inv, NULL, NULL, &tdata); + const pjmedia_sdp_session *offer = NULL; + + if (inv->timer->with_sdp) { + pjmedia_sdp_neg_get_active_local(inv->neg, &offer); + } + status = pjsip_inv_update(inv, NULL, offer, &tdata); } else { /* Create re-INVITE without modifying session */ pjsip_msg_body *body; @@ -384,8 +402,8 @@ void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) } pj_gettimeofday(&now); - PJ_LOG(4, (inv->pool->obj_name, - "Refresh session after %ds (expiration period=%ds)", + PJ_LOG(4, (inv->pool->obj_name, + "Refreshing session after %ds (expiration period=%ds)", (now.sec-inv->timer->last_refresh.sec), inv->timer->setting.sess_expires)); } else { @@ -414,23 +432,19 @@ void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) /* Print error message, if any */ if (status != PJ_SUCCESS) { - char errmsg[PJ_ERR_MSG_SIZE]; - if (tdata) pjsip_tx_data_dec_ref(tdata); - pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(2, (inv->pool->obj_name, "Session timer fails in %s session, " - "err code=%d (%s)", - (as_refresher? "refreshing" : - "terminating"), - status, errmsg)); + PJ_PERROR(2, (inv->pool->obj_name, status, + "Error in %s session timer", + (as_refresher? "refreshing" : "terminating"))); } } /* Start Session Timers */ static void start_timer(pjsip_inv_session *inv) { + const pj_str_t UPDATE = { "UPDATE", 6 }; pjsip_timer *timer = inv->timer; pj_time_val delay = {0}; @@ -438,6 +452,14 @@ static void start_timer(pjsip_inv_session *inv) stop_timer(inv); + inv->timer->use_update = + (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, + &UPDATE) == PJSIP_DIALOG_CAP_SUPPORTED); + if (!inv->timer->use_update) { + /* INVITE always needs SDP */ + inv->timer->with_sdp = PJ_TRUE; + } + pj_timer_entry_init(&timer->timer, 1, /* id */ inv, /* user data */ @@ -837,14 +859,30 @@ PJ_DEF(pj_status_t) pjsip_timer_process_resp(pjsip_inv_session *inv, */ inv->timer->refresher = TR_UAC; - PJ_TODO(CHECK_IF_REMOTE_SUPPORT_UPDATE); - /* Remember our role in this transaction */ inv->timer->role = PJSIP_ROLE_UAC; /* Finally, set active flag and start the Session Timers */ inv->timer->active = PJ_TRUE; start_timer(inv); + + } else if (pjsip_method_cmp(&rdata->msg_info.cseq->method, + &pjsip_update_method) == 0 && + msg->line.status.code >= 400 && msg->line.status.code < 600) + { + /* This is to handle error response to previous UPDATE that was + * sent without SDP. In this case, retry sending UPDATE but + * with SDP this time. + * Note: the additional expressions are to check that the + * UPDATE was really the one sent by us, not by other + * call components (e.g. to change codec) + */ + if (inv->timer->timer.id == 0 && inv->timer->use_update && + inv->timer->with_sdp == PJ_FALSE) + { + inv->timer->with_sdp = PJ_TRUE; + timer_cb(NULL, &inv->timer->timer); + } } return PJ_SUCCESS; |