summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_pjsip.c9
-rw-r--r--include/asterisk/res_pjsip_session.h4
-rw-r--r--res/res_pjsip_session.c39
3 files changed, 47 insertions, 5 deletions
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index a92816431..5bf339ee9 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -2052,11 +2052,16 @@ static int hangup(void *data)
struct ast_sip_session *session = channel->session;
int cause = h_data->cause;
- ast_sip_session_terminate(session, cause);
+ /*
+ * It's possible that session_terminate might cause the session to be destroyed
+ * immediately so we need to keep a reference to it so we can NULL session->channel
+ * afterwards.
+ */
+ ast_sip_session_terminate(ao2_bump(session), cause);
clear_session_and_channel(session, ast, pvt);
+ ao2_cleanup(session);
ao2_cleanup(channel);
ao2_cleanup(h_data);
-
return 0;
}
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index d4d3f705a..10e55f133 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -459,6 +459,10 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
*
* \param session The session to terminate
* \param response The response code to use for termination if possible
+ *
+ * \warning Calling this function MAY cause the last session reference to be
+ * released and the session destructor to be called. If you need to do something
+ * with session after this call, be sure to bump the ref count before calling terminate.
*/
void ast_sip_session_terminate(struct ast_sip_session *session, int response);
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 3034652a5..2613a74bc 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1302,9 +1302,19 @@ static void session_destructor(void *obj)
struct ast_sip_session *session = obj;
struct ast_sip_session_supplement *supplement;
struct ast_sip_session_delayed_request *delay;
+ const char *endpoint_name = session->endpoint ?
+ ast_sorcery_object_get_id(session->endpoint) : "<none>";
- ast_debug(3, "Destroying SIP session with endpoint %s\n",
- session->endpoint ? ast_sorcery_object_get_id(session->endpoint) : "<none>");
+ ast_debug(3, "Destroying SIP session with endpoint %s\n", endpoint_name);
+
+ ast_test_suite_event_notify("SESSION_DESTROYING",
+ "Endpoint: %s\r\n"
+ "AOR: %s\r\n"
+ "Contact: %s"
+ , endpoint_name
+ , session->aor ? ast_sorcery_object_get_id(session->aor) : "<none>"
+ , session->contact ? ast_sorcery_object_get_id(session->contact) : "<none>"
+ );
while ((supplement = AST_LIST_REMOVE_HEAD(&session->supplements, next))) {
if (supplement->session_destroy) {
@@ -1333,6 +1343,8 @@ static void session_destructor(void *obj)
if (session->inv_session) {
pjsip_dlg_dec_session(session->inv_session->dlg, &session_module);
}
+
+ ast_test_suite_event_notify("SESSION_DESTROYED", "Endpoint: %s", endpoint_name);
}
static int add_supplements(struct ast_sip_session *session)
@@ -1793,6 +1805,9 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
return ret_session;
}
+static int session_end(void *vsession);
+static int session_end_completion(void *vsession);
+
void ast_sip_session_terminate(struct ast_sip_session *session, int response)
{
pj_status_t status;
@@ -1809,7 +1824,25 @@ void ast_sip_session_terminate(struct ast_sip_session *session, int response)
switch (session->inv_session->state) {
case PJSIP_INV_STATE_NULL:
- pjsip_inv_terminate(session->inv_session, response, PJ_TRUE);
+ if (!session->inv_session->invite_tsx) {
+ /*
+ * Normally, it's pjproject's transaction cleanup that ultimately causes the
+ * final session reference to be released but if both STATE and invite_tsx are NULL,
+ * we never created a transaction in the first place. In this case, we need to
+ * do the cleanup ourselves.
+ */
+ /* Transfer the inv_session session reference to the session_end_task */
+ session->inv_session->mod_data[session_module.id] = NULL;
+ pjsip_inv_terminate(session->inv_session, response, PJ_TRUE);
+ session_end(session);
+ /*
+ * session_end_completion will cleanup the final session reference unless
+ * ast_sip_session_terminate's caller is holding one.
+ */
+ session_end_completion(session);
+ } else {
+ pjsip_inv_terminate(session->inv_session, response, PJ_TRUE);
+ }
break;
case PJSIP_INV_STATE_CONFIRMED:
if (session->inv_session->invite_tsx) {