summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-09-24 10:10:15 +0000
committerBenny Prijono <bennylp@teluu.com>2008-09-24 10:10:15 +0000
commitc2476cfffd0fa7f90ac72295de24ba6dea4ea161 (patch)
tree0a76d346be96d75d14e4ce627f689f76c1914a07
parent8fa5079ad7ee39493cd3231de2030335ad56eccc (diff)
Ticket #635: Disconnect the other call leg when multiple 2xx/OK responses to INVITE are received due to forking
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2315 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h14
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h2
-rw-r--r--pjsip/src/pjsip/sip_dialog.c74
-rw-r--r--pjsip/src/pjsip/sip_ua_layer.c4
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c41
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c8
6 files changed, 126 insertions, 17 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index e377e059..43d28a88 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1230,6 +1230,20 @@ typedef struct pjsua_config
int srtp_secure_signaling;
#endif
+ /**
+ * Disconnect other call legs when more than one 2xx responses for
+ * outgoing INVITE are received due to forking. Currently the library
+ * is not able to handle simultaneous forked media, so disconnecting
+ * the other call legs is necessary.
+ *
+ * With this setting enabled, the library will handle only one of the
+ * connected call leg, and the other connected call legs will be
+ * disconnected.
+ *
+ * Default: PJ_TRUE (only disable this setting for testing purposes).
+ */
+ pj_bool_t hangup_forked_call;
+
} pjsua_config;
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index ef1487a4..1332a5ae 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -498,7 +498,7 @@ void pjsua_parse_media_type( pj_pool_t *pool,
void pjsua_init_tpselector(pjsua_transport_id tp_id,
pjsip_tpselector *sel);
-
+pjsip_dialog* on_dlg_forked(pjsip_dialog *first_set, pjsip_rx_data *res);
pj_status_t acquire_call(const char *title,
pjsua_call_id call_id,
pjsua_call **p_call,
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index 2b3203f4..4489cb8e 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -580,31 +580,39 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
pjsip_dialog **new_dlg )
{
pjsip_dialog *dlg;
- const pjsip_route_hdr *r;
+ const pjsip_msg *msg = rdata->msg_info.msg;
+ const pjsip_hdr *end_hdr, *hdr;
+ const pjsip_contact_hdr *contact;
pj_status_t status;
/* Check arguments. */
PJ_ASSERT_RETURN(first_dlg && rdata && new_dlg, PJ_EINVAL);
/* rdata must be response message. */
- PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG,
+ PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG,
PJSIP_ENOTRESPONSEMSG);
/* Status code MUST be 1xx (but not 100), or 2xx */
- status = rdata->msg_info.msg->line.status.code;
+ status = msg->line.status.code;
PJ_ASSERT_RETURN( (status/100==1 && status!=100) ||
(status/100==2), PJ_EBUG);
/* To tag must present in the response. */
PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG);
+ /* Find Contact header in the response */
+ contact = (const pjsip_contact_hdr*)
+ pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);
+ if (contact == NULL)
+ return PJSIP_EMISSINGHDR;
+
/* Create the dialog. */
status = create_dialog((pjsip_user_agent*)first_dlg->ua, &dlg);
if (status != PJ_SUCCESS)
return status;
- /* Clone remote target. */
- dlg->target = (pjsip_uri*) pjsip_uri_clone(dlg->pool, first_dlg->target);
+ /* Set remote target from the response. */
+ dlg->target = (pjsip_uri*) pjsip_uri_clone(dlg->pool, contact->uri);
/* Clone local info. */
dlg->local.info = (pjsip_fromto_hdr*)
@@ -636,7 +644,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
dlg->role = PJSIP_ROLE_UAC;
/* Dialog state depends on the response. */
- status = rdata->msg_info.msg->line.status.code/100;
+ status = msg->line.status.code/100;
if (status == 1 || status == 2)
dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;
else {
@@ -651,17 +659,18 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
dlg->call_id = (pjsip_cid_hdr*)
pjsip_hdr_clone(dlg->pool, first_dlg->call_id);
- /* Duplicate Route-Set. */
+ /* Get route-set from the response. */
pj_list_init(&dlg->route_set);
- r = first_dlg->route_set.next;
- while (r != &first_dlg->route_set) {
- pjsip_route_hdr *h;
-
- h = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, r);
- pj_list_push_back(&dlg->route_set, h);
-
- r = r->next;
+ end_hdr = &msg->hdr;
+ for (hdr=msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *r;
+ r = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(r);
+ pj_list_push_back(&dlg->route_set, r);
+ }
}
+
//dlg->route_set_frozen = PJ_TRUE;
/* Clone client authentication session. */
@@ -1813,6 +1822,41 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
break;
}
+ /* Handle the case of forked response, when the application creates
+ * the forked dialog but not the invite session. In this case, the
+ * forked 200/OK response will be unhandled, and we must send ACK
+ * here.
+ */
+ if (dlg->usage_cnt==0) {
+ pj_status_t status;
+
+ if (rdata->msg_info.cseq->method.id==PJSIP_INVITE_METHOD &&
+ rdata->msg_info.msg->line.status.code/100 == 2)
+ {
+ pjsip_tx_data *ack;
+
+ status = pjsip_dlg_create_request(dlg, &pjsip_ack_method,
+ rdata->msg_info.cseq->cseq,
+ &ack);
+ if (status == PJ_SUCCESS)
+ status = pjsip_dlg_send_request(dlg, ack, -1, NULL);
+ } else if (rdata->msg_info.msg->line.status.code==401 ||
+ rdata->msg_info.msg->line.status.code==407)
+ {
+ pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+ pjsip_tx_data *tdata;
+
+ status = pjsip_auth_clt_reinit_req( &dlg->auth_sess,
+ rdata, tsx->last_tx,
+ &tdata);
+
+ if (status == PJ_SUCCESS) {
+ /* Re-send request. */
+ status = pjsip_dlg_send_request(dlg, tdata, -1, NULL);
+ }
+ }
+ }
+
/* Unhandled response does not necessarily mean error because
dialog usages may choose to process the transaction state instead.
if (i==dlg->usage_cnt) {
diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c
index 21ff5f39..a614d16c 100644
--- a/pjsip/src/pjsip/sip_ua_layer.c
+++ b/pjsip/src/pjsip/sip_ua_layer.c
@@ -832,6 +832,10 @@ retry_on_deadlock:
if (mod_ua.param.on_dlg_forked) {
dlg = (*mod_ua.param.on_dlg_forked)(dlg_set->dlg_list.next,
rdata);
+ if (dlg == NULL) {
+ pj_mutex_unlock(mod_ua.mutex);
+ return PJ_TRUE;
+ }
} else {
dlg = dlg_set->dlg_list.next;
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 7eb061cc..546be666 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -2943,6 +2943,47 @@ static void pjsua_call_on_forked( pjsip_inv_session *inv,
/*
+ * Callback from UA layer when forked dialog response is received.
+ */
+pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
+{
+ if (dlg->uac_has_2xx &&
+ res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
+ pjsip_rdata_get_tsx(res) == NULL &&
+ res->msg_info.msg->line.status.code/100 == 2)
+ {
+ pjsip_dialog *forked_dlg;
+ pjsip_tx_data *bye;
+ pj_status_t status;
+
+ /* Create forked dialog */
+ status = pjsip_dlg_fork(dlg, res, &forked_dlg);
+ if (status != PJ_SUCCESS)
+ return NULL;
+
+ pjsip_dlg_inc_lock(forked_dlg);
+
+ /* Disconnect the call */
+ status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
+ -1, &bye);
+ if (status == PJ_SUCCESS) {
+ status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
+ }
+
+ pjsip_dlg_dec_lock(forked_dlg);
+
+ if (status != PJ_SUCCESS) {
+ return NULL;
+ }
+
+ return forked_dlg;
+
+ } else {
+ return dlg;
+ }
+}
+
+/*
* Disconnect call upon error.
*/
static void call_disconnect( pjsip_inv_session *inv,
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 40830d73..650217a2 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -96,6 +96,7 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP;
cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING;
#endif
+ cfg->hangup_forked_call = PJ_TRUE;
}
PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
@@ -622,6 +623,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
pjsua_config default_cfg;
pjsua_media_config default_media_cfg;
const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
+ pjsip_ua_init_param ua_init_param;
pj_status_t status;
@@ -694,7 +696,11 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
/* Initialize UA layer module: */
- status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
+ pj_bzero(&ua_init_param, sizeof(ua_init_param));
+ if (ua_cfg->hangup_forked_call) {
+ ua_init_param.on_dlg_forked = &on_dlg_forked;
+ }
+ status = pjsip_ua_init_module( pjsua_var.endpt, &ua_init_param);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);