[Asterisk-code-review] res pjsip refer/session: Calls dropped during transfer (asterisk[certified/13.1])

Kevin Harwell asteriskteam at digium.com
Tue Jun 13 14:32:21 CDT 2017


Kevin Harwell has uploaded this change for review. ( https://gerrit.asterisk.org/5822


Change subject: res_pjsip_refer/session: Calls dropped during transfer
......................................................................

res_pjsip_refer/session: Calls dropped during transfer

When doing an attended transfer it's possible for the transferer, after
receiving an accepted response from Asterisk, to send a BYE to Asterisk,
which can then be processed before Asterisk has time to start and/or
complete the transfer process. This of course causes the transfer to not
complete successfully, thus dropping the call.

This patch makes it so any BYEs received from the transferer, after the REFER,
that initiate a session end are deferred until the transfer is complete. This
allows the channel that would have otherwise been hung up by Asterisk to
remain available throughout the transfer process.

ASTERISK-27053 #close

Change-Id: I43586db79079457d92d71f1fd993be9a3b409d5a
---
M include/asterisk/res_pjsip_session.h
M res/res_pjsip_refer.c
M res/res_pjsip_session.c
M res/res_pjsip_session.exports.in
4 files changed, 73 insertions(+), 2 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/22/5822/1

diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 7fbeddc..9d0c915 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -145,6 +145,10 @@
 	pjsip_rx_data *deferred_reinvite;
 	/*! Current T.38 state */
 	enum ast_sip_session_t38state t38state;
+	/*! Whether the end of the session should be deferred */
+	unsigned int defer_end:1;
+	/*! Session end (remote hangup) requested while termination deferred */
+	unsigned int ended_while_deferred:1;
 };
 
 typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -459,6 +463,20 @@
 void ast_sip_session_defer_termination(struct ast_sip_session *session);
 
 /*!
+ * \brief Defer the end of a session
+ *
+ * \param session The session to defer
+ */
+void ast_sip_session_defer_end(struct ast_sip_session *session);
+
+/*!
+ * \brief End the session if it had been previously deferred
+ *
+ * \param session The session to end if it had been deferred
+ */
+void ast_sip_session_end_if_deferred(struct ast_sip_session *session);
+
+/*!
  * \brief Register an SDP handler
  *
  * An SDP handler is responsible for parsing incoming SDP streams and ensuring that
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 13ca01a..59a38ad 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -488,6 +488,8 @@
 		}
 	}
 
+	ast_sip_session_end_if_deferred(attended->transferer);
+
 	return 0;
 }
 
@@ -656,8 +658,11 @@
 			return 500;
 		}
 
+		ast_sip_session_defer_end(session);
+
 		/* Push it to the other session, which will have both channels with minimal locking */
 		if (ast_sip_push_task(other_session->serializer, refer_attended, attended)) {
+			ast_sip_session_end_if_deferred(session);
 			ao2_cleanup(attended);
 			return 500;
 		}
@@ -669,6 +674,7 @@
 	} else {
 		const char *context = pbx_builtin_getvar_helper(session->channel, "TRANSFER_CONTEXT");
 		struct refer_blind refer = { 0, };
+		enum ast_transfer_result transfer_result;
 
 		if (ast_strlen_zero(context)) {
 			context = session->endpoint->context;
@@ -686,7 +692,12 @@
 		refer.replaces = replaces;
 		refer.refer_to = target_uri;
 
-		switch (ast_bridge_transfer_blind(1, session->channel, "external_replaces", context, refer_blind_callback, &refer)) {
+		ast_sip_session_defer_end(session);
+		transfer_result = ast_bridge_transfer_blind(1, session->channel,
+			"external_replaces", context, refer_blind_callback, &refer);
+		ast_sip_session_end_if_deferred(session);
+
+		switch (transfer_result) {
 		case AST_BRIDGE_TRANSFER_INVALID:
 			return 400;
 		case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
@@ -697,6 +708,8 @@
 			ast_sip_session_defer_termination(session);
 			return 200;
 		}
+
+		ast_sip_session_end_if_deferred(session);
 
 		return 503;
 	}
@@ -710,6 +723,7 @@
 	const char *context;
 	char exten[AST_MAX_EXTENSION];
 	struct refer_blind refer = { 0, };
+	enum ast_transfer_result transfer_result;
 
 	/* If no explicit transfer context has been provided use their configured context */
 	context = pbx_builtin_getvar_helper(session->channel, "TRANSFER_CONTEXT");
@@ -730,7 +744,12 @@
 	refer.rdata = rdata;
 	refer.refer_to = target;
 
-	switch (ast_bridge_transfer_blind(1, session->channel, exten, context, refer_blind_callback, &refer)) {
+	ast_sip_session_defer_end(session);
+	transfer_result = ast_bridge_transfer_blind(1, session->channel, exten,
+		context, refer_blind_callback, &refer);
+	ast_sip_session_end_if_deferred(session);
+
+	switch (transfer_result) {
 	case AST_BRIDGE_TRANSFER_INVALID:
 		return 400;
 	case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index a2b30a1..2832b36 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1653,6 +1653,8 @@
 	return session;
 }
 
+static int session_end(struct ast_sip_session *session);
+
 void ast_sip_session_terminate(struct ast_sip_session *session, int response)
 {
 	pj_status_t status;
@@ -1730,6 +1732,30 @@
 
 	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &session->scheduled_termination, &delay) != PJ_SUCCESS) {
 		ao2_ref(session, -1);
+	}
+}
+
+void ast_sip_session_defer_end(struct ast_sip_session *session)
+{
+	ast_assert(!session->defer_end);
+
+	session->defer_end = 1;
+	session->ended_while_deferred = 0;
+}
+
+void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
+{
+	if (!session->defer_end) {
+		return;
+	}
+
+	session->defer_end = 0;
+
+	if (session->ended_while_deferred) {
+		/* Complete the session end started by the remote hangup. */
+		ast_debug(3, "Ending session (%p) after being deferred\n", session);
+		session->ended_while_deferred = 0;
+		session_end(session);
 	}
 }
 
@@ -2349,6 +2375,12 @@
 	}
 
 	if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+		if (session->defer_end) {
+			ast_debug(3, "Deferring session (%p) end\n", session);
+			session->ended_while_deferred = 1;
+			return;
+		}
+
 		session_end(session);
 	}
 }
diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in
index 5a21b1d..4af9933 100644
--- a/res/res_pjsip_session.exports.in
+++ b/res/res_pjsip_session.exports.in
@@ -2,6 +2,8 @@
 	global:
 		LINKER_SYMBOL_PREFIXast_sip_session_terminate;
 		LINKER_SYMBOL_PREFIXast_sip_session_defer_termination;
+		LINKER_SYMBOL_PREFIXast_sip_session_defer_end;
+		LINKER_SYMBOL_PREFIXast_sip_session_end_if_deferred;
 		LINKER_SYMBOL_PREFIXast_sip_session_register_sdp_handler;
 		LINKER_SYMBOL_PREFIXast_sip_session_unregister_sdp_handler;
 		LINKER_SYMBOL_PREFIXast_sip_session_register_supplement;

-- 
To view, visit https://gerrit.asterisk.org/5822
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: certified/13.1
Gerrit-MessageType: newchange
Gerrit-Change-Id: I43586db79079457d92d71f1fd993be9a3b409d5a
Gerrit-Change-Number: 5822
Gerrit-PatchSet: 1
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20170613/dc331cdb/attachment.html>


More information about the asterisk-code-review mailing list