<p>Kevin Harwell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/5821">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_refer/session: Calls dropped during transfer<br><br>When doing an attended transfer it's possible for the transferer, after<br>receiving an accepted response from Asterisk, to send a BYE to Asterisk,<br>which can then be processed before Asterisk has time to start and/or<br>complete the transfer process. This of course causes the transfer to not<br>complete successfully, thus dropping the call.<br><br>This patch makes it so any BYEs received from the transferer, after the REFER,<br>that initiate a session end are deferred until the transfer is complete. This<br>allows the channel that would have otherwise been hung up by Asterisk to<br>remain available throughout the transfer process.<br><br>ASTERISK-27053 #close<br><br>Change-Id: I43586db79079457d92d71f1fd993be9a3b409d5a<br>---<br>M include/asterisk/res_pjsip_session.h<br>M res/res_pjsip_refer.c<br>M res/res_pjsip_session.c<br>M res/res_pjsip_session.exports.in<br>4 files changed, 46 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/21/5821/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h<br>index 7e65e6d..c9a41f8 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -151,6 +151,10 @@<br>        struct ast_sip_aor *aor;<br>      /*! From header saved at invite creation */<br>   pjsip_fromto_hdr *saved_from_hdr;<br>+    /*! Whether the end of the session should be deferred */<br>+     unsigned int defer_end:1;<br>+    /*! Session end (remote hangup) requested while termination deferred */<br>+      unsigned int ended_while_deferred:1;<br> };<br> <br> typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);<br>@@ -476,6 +480,13 @@<br> void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session);<br> <br> /*!<br>+ * \brief End the session if it had been previously deferred<br>+ *<br>+ * \param session The session to end if it had been deferred<br>+ */<br>+void ast_sip_session_end_if_deferred(struct ast_sip_session *session);<br>+<br>+/*!<br>  * \brief Register an SDP handler<br>  *<br>  * An SDP handler is responsible for parsing incoming SDP streams and ensuring that<br>diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c<br>index 99295d5..06f709e 100644<br>--- a/res/res_pjsip_refer.c<br>+++ b/res/res_pjsip_refer.c<br>@@ -539,6 +539,7 @@<br>                }<br>     }<br> <br>+ ast_sip_session_end_if_deferred(attended->transferer);<br>     if (response != 200) {<br>                if (!ast_sip_push_task(attended->transferer->serializer,<br>                        defer_termination_cancel, attended->transferer)) {<br>@@ -757,6 +758,7 @@<br> <br>                 /* Push it to the other session, which will have both channels with minimal locking */<br>                if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {<br>+                        ast_sip_session_end_if_deferred(session);<br>                     ast_sip_session_defer_termination_cancel(session);<br>                    ao2_cleanup(attended);<br>                        return 500;<br>@@ -794,9 +796,12 @@<br> <br>          response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,<br>                   "external_replaces", context, refer_blind_callback, &refer));<br>+<br>+               ast_sip_session_end_if_deferred(session);<br>             if (response != 200) {<br>                        ast_sip_session_defer_termination_cancel(session);<br>            }<br>+<br>          return response;<br>      }<br> }<br>@@ -841,9 +846,12 @@<br> <br>        response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,<br>           exten, context, refer_blind_callback, &refer));<br>+<br>+       ast_sip_session_end_if_deferred(session);<br>     if (response != 200) {<br>                ast_sip_session_defer_termination_cancel(session);<br>    }<br>+<br>  return response;<br> }<br> <br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index 60850f0..083635d 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -1883,6 +1883,9 @@<br> <br>        session->defer_terminate = 1;<br> <br>+  session->defer_end = 1;<br>+   session->ended_while_deferred = 0;<br>+<br>      session->scheduled_termination.id = 0;<br>     ao2_ref(session, +1);<br>         session->scheduled_termination.user_data = session;<br>@@ -1920,6 +1923,7 @@<br>                 /* Already canceled or timer fired. */<br>                return;<br>       }<br>+<br>  session->defer_terminate = 0;<br> <br>   if (session->terminate_while_deferred) {<br>@@ -1929,6 +1933,22 @@<br> <br>        /* Stop the termination timer if it is still running. */<br>      sip_session_defer_termination_stop_timer(session);<br>+}<br>+<br>+void ast_sip_session_end_if_deferred(struct ast_sip_session *session)<br>+{<br>+        if (!session->defer_end) {<br>+                return;<br>+      }<br>+<br>+ session->defer_end = 0;<br>+<br>+        if (session->ended_while_deferred) {<br>+              /* Complete the session end started by the remote hangup. */<br>+         ast_debug(3, "Ending session (%p) after being deferred\n", session);<br>+               session->ended_while_deferred = 0;<br>+                session_end(session);<br>+        }<br> }<br> <br> struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg)<br>@@ -2620,6 +2640,12 @@<br>      }<br> <br>  if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {<br>+         if (session->defer_end) {<br>+                 ast_debug(3, "Deferring session (%p) end\n", session);<br>+                     session->ended_while_deferred = 1;<br>+                        return;<br>+              }<br>+<br>          if (ast_sip_push_task(session->serializer, session_end, session)) {<br>                        /* Do it anyway even though this is not the right thread. */<br>                  session_end(session);<br>diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in<br>index a39485e..fdfc5fb 100644<br>--- a/res/res_pjsip_session.exports.in<br>+++ b/res/res_pjsip_session.exports.in<br>@@ -3,6 +3,7 @@<br>               LINKER_SYMBOL_PREFIXast_sip_session_terminate;<br>                LINKER_SYMBOL_PREFIXast_sip_session_defer_termination;<br>                LINKER_SYMBOL_PREFIXast_sip_session_defer_termination_cancel;<br>+                LINKER_SYMBOL_PREFIXast_sip_session_end_if_deferred;<br>          LINKER_SYMBOL_PREFIXast_sip_session_register_sdp_handler;<br>             LINKER_SYMBOL_PREFIXast_sip_session_unregister_sdp_handler;<br>           LINKER_SYMBOL_PREFIXast_sip_session_register_supplement;<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/5821">change 5821</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/5821"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: certified/13.13 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I43586db79079457d92d71f1fd993be9a3b409d5a </div>
<div style="display:none"> Gerrit-Change-Number: 5821 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>