<p>Kevin Harwell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/5822">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, 73 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/22/5822/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 7fbeddc..9d0c915 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -145,6 +145,10 @@<br>        pjsip_rx_data *deferred_reinvite;<br>     /*! Current T.38 state */<br>     enum ast_sip_session_t38state t38state;<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>@@ -459,6 +463,20 @@<br> void ast_sip_session_defer_termination(struct ast_sip_session *session);<br> <br> /*!<br>+ * \brief Defer the end of a session<br>+ *<br>+ * \param session The session to defer<br>+ */<br>+void ast_sip_session_defer_end(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 13ca01a..59a38ad 100644<br>--- a/res/res_pjsip_refer.c<br>+++ b/res/res_pjsip_refer.c<br>@@ -488,6 +488,8 @@<br>                }<br>     }<br> <br>+ ast_sip_session_end_if_deferred(attended->transferer);<br>+<br>  return 0;<br> }<br> <br>@@ -656,8 +658,11 @@<br>                        return 500;<br>           }<br> <br>+         ast_sip_session_defer_end(session);<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, attended)) {<br>+                     ast_sip_session_end_if_deferred(session);<br>                     ao2_cleanup(attended);<br>                        return 500;<br>           }<br>@@ -669,6 +674,7 @@<br>        } else {<br>              const char *context = pbx_builtin_getvar_helper(session->channel, "TRANSFER_CONTEXT");<br>           struct refer_blind refer = { 0, };<br>+           enum ast_transfer_result transfer_result;<br> <br>          if (ast_strlen_zero(context)) {<br>                       context = session->endpoint->context;<br>@@ -686,7 +692,12 @@<br>             refer.replaces = replaces;<br>            refer.refer_to = target_uri;<br> <br>-              switch (ast_bridge_transfer_blind(1, session->channel, "external_replaces", context, refer_blind_callback, &refer)) {<br>+               ast_sip_session_defer_end(session);<br>+          transfer_result = ast_bridge_transfer_blind(1, session->channel,<br>+                  "external_replaces", context, refer_blind_callback, &refer);<br>+           ast_sip_session_end_if_deferred(session);<br>+<br>+         switch (transfer_result) {<br>            case AST_BRIDGE_TRANSFER_INVALID:<br>                     return 400;<br>           case AST_BRIDGE_TRANSFER_NOT_PERMITTED:<br>@@ -697,6 +708,8 @@<br>                  ast_sip_session_defer_termination(session);<br>                   return 200;<br>           }<br>+<br>+         ast_sip_session_end_if_deferred(session);<br> <br>          return 503;<br>   }<br>@@ -710,6 +723,7 @@<br>        const char *context;<br>  char exten[AST_MAX_EXTENSION];<br>        struct refer_blind refer = { 0, };<br>+   enum ast_transfer_result transfer_result;<br> <br>  /* If no explicit transfer context has been provided use their configured context */<br>  context = pbx_builtin_getvar_helper(session->channel, "TRANSFER_CONTEXT");<br>@@ -730,7 +744,12 @@<br>         refer.rdata = rdata;<br>  refer.refer_to = target;<br> <br>-  switch (ast_bridge_transfer_blind(1, session->channel, exten, context, refer_blind_callback, &refer)) {<br>+       ast_sip_session_defer_end(session);<br>+  transfer_result = ast_bridge_transfer_blind(1, session->channel, exten,<br>+           context, refer_blind_callback, &refer);<br>+  ast_sip_session_end_if_deferred(session);<br>+<br>+ switch (transfer_result) {<br>    case AST_BRIDGE_TRANSFER_INVALID:<br>             return 400;<br>   case AST_BRIDGE_TRANSFER_NOT_PERMITTED:<br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index a2b30a1..2832b36 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -1653,6 +1653,8 @@<br>   return session;<br> }<br> <br>+static int session_end(struct ast_sip_session *session);<br>+<br> void ast_sip_session_terminate(struct ast_sip_session *session, int response)<br> {<br>      pj_status_t status;<br>@@ -1730,6 +1732,30 @@<br> <br>        if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &session->scheduled_termination, &delay) != PJ_SUCCESS) {<br>             ao2_ref(session, -1);<br>+        }<br>+}<br>+<br>+void ast_sip_session_defer_end(struct ast_sip_session *session)<br>+{<br>+       ast_assert(!session->defer_end);<br>+<br>+       session->defer_end = 1;<br>+   session->ended_while_deferred = 0;<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>@@ -2349,6 +2375,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>          session_end(session);<br>         }<br> }<br>diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in<br>index 5a21b1d..4af9933 100644<br>--- a/res/res_pjsip_session.exports.in<br>+++ b/res/res_pjsip_session.exports.in<br>@@ -2,6 +2,8 @@<br>       global:<br>               LINKER_SYMBOL_PREFIXast_sip_session_terminate;<br>                LINKER_SYMBOL_PREFIXast_sip_session_defer_termination;<br>+               LINKER_SYMBOL_PREFIXast_sip_session_defer_end;<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/5822">change 5822</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/5822"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: certified/13.1 </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: 5822 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>