summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c52
-rw-r--r--pjsip/src/pjsip-ua/sip_timer.c98
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c19
-rw-r--r--tests/pjsua/scripts-sipp/uas-timer-reinvite.xml108
-rw-r--r--tests/pjsua/scripts-sipp/uas-timer-update.xml126
5 files changed, 352 insertions, 51 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;
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 84012080..fd8163a0 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -855,10 +855,25 @@ static void on_ice_complete(pjmedia_transport *tp,
pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
&pjsua_var.calls[id].med_rtp_addr))
{
+ pj_bool_t use_update;
+ const pj_str_t STR_UPDATE = { "UPDATE", 6 };
+ pjsip_dialog_cap_status support_update;
+ pjsip_dialog *dlg;
+
+ dlg = pjsua_var.calls[id].inv->dlg;
+ support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
+ NULL, &STR_UPDATE);
+ use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
+
PJ_LOG(4,(THIS_FILE,
"ICE default transport address has changed for "
- "call %d, sending UPDATE", id));
- pjsua_call_update(id, 0, NULL);
+ "call %d, sending %s", id,
+ (use_update ? "UPDATE" : "re-INVITE")));
+
+ if (use_update)
+ pjsua_call_update(id, 0, NULL);
+ else
+ pjsua_call_reinvite(id, 0, NULL);
}
}
break;
diff --git a/tests/pjsua/scripts-sipp/uas-timer-reinvite.xml b/tests/pjsua/scripts-sipp/uas-timer-reinvite.xml
new file mode 100644
index 00000000..fe5169bb
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-timer-reinvite.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- This program is free software; you can redistribute it and/or -->
+<!-- modify it under the terms of the GNU General Public License as -->
+<!-- published by the Free Software Foundation; either version 2 of the -->
+<!-- License, or (at your option) any later version. -->
+<!-- -->
+<!-- This program is distributed in the hope that it will be useful, -->
+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
+<!-- GNU General Public License for more details. -->
+<!-- -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with this program; if not, write to the -->
+<!-- Free Software Foundation, Inc., -->
+<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
+
+
+<!-- -->
+<!-- Session timer where UAS doesn't indicate support for UPDATE. -->
+<!-- In this case, UAC MUST use re-INVITE with SDP. -->
+
+<scenario name="Basic UAS responder">
+ <recv request="INVITE" crlf="true">
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Require: timer
+ Session-Expires: 90;refresher=uac
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=Some-UserAgent 68 210 IN IP4 [local_ip]
+ s=SIP Call
+ c=IN IP4 [local_ip]
+ t=0 0
+ m=audio 17294 RTP/AVP 0 101
+ c=IN IP4 [local_ip]
+ a=rtpmap:101 telephone-event/8000
+ a=fmtp:101 0-16
+ ]]>
+ </send>
+
+ <recv request="ACK"
+ optional="true"
+ rtd="true"
+ crlf="true">
+ </recv>
+
+ <recv request="INVITE" crlf="true">
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Require: timer
+ Session-Expires: 90;refresher=uac
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=Some-UserAgent 68 210 IN IP4 [local_ip]
+ s=SIP Call
+ c=IN IP4 [local_ip]
+ t=0 0
+ m=audio 17294 RTP/AVP 0 101
+ c=IN IP4 [local_ip]
+ a=rtpmap:101 telephone-event/8000
+ a=fmtp:101 0-16
+ ]]>
+ </send>
+
+ <recv request="ACK"
+ rtd="true"
+ crlf="true">
+ </recv>
+
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
diff --git a/tests/pjsua/scripts-sipp/uas-timer-update.xml b/tests/pjsua/scripts-sipp/uas-timer-update.xml
new file mode 100644
index 00000000..11a59734
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-timer-update.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- This program is free software; you can redistribute it and/or -->
+<!-- modify it under the terms of the GNU General Public License as -->
+<!-- published by the Free Software Foundation; either version 2 of the -->
+<!-- License, or (at your option) any later version. -->
+<!-- -->
+<!-- This program is distributed in the hope that it will be useful, -->
+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
+<!-- GNU General Public License for more details. -->
+<!-- -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with this program; if not, write to the -->
+<!-- Free Software Foundation, Inc., -->
+<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
+
+
+<!-- -->
+<!-- Session timer where UAS incidates support for UPDATE. -->
+<!-- In this case, UAC will first use empty UPDATE, which we -->
+<!-- will reply with 400. UAC MUST retry sending UPDATE with SDP. -->
+
+<scenario name="Basic UAS responder">
+ <recv request="INVITE" crlf="true">
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Allow: UPDATE, INVITE
+ Require: timer
+ Session-Expires: 90;refresher=uac
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=Some-UserAgent 68 210 IN IP4 [local_ip]
+ s=SIP Call
+ c=IN IP4 [local_ip]
+ t=0 0
+ m=audio 17294 RTP/AVP 0 101
+ c=IN IP4 [local_ip]
+ a=rtpmap:101 telephone-event/8000
+ a=fmtp:101 0-16
+ ]]>
+ </send>
+
+ <recv request="ACK"
+ optional="true"
+ rtd="true"
+ crlf="true">
+ </recv>
+
+ <recv request="UPDATE" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+
+ SIP/2.0 400 Want SDP Body
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Allow: INVITE
+ Require: timer
+ Session-Expires: 90;refresher=uac
+ Content-Length: 0
+ ]]>
+ </send>
+
+ <recv request="UPDATE" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Allow: INVITE
+ Require: timer
+ Session-Expires: 90;refresher=uac
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=Some-UserAgent 68 210 IN IP4 [local_ip]
+ s=SIP Call
+ c=IN IP4 [local_ip]
+ t=0 0
+ m=audio 17294 RTP/AVP 0 101
+ c=IN IP4 [local_ip]
+ a=rtpmap:101 telephone-event/8000
+ a=fmtp:101 0-16
+ ]]>
+ </send>
+
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+