summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2015-04-11 15:39:29 -0600
committerMatt Jordan <mjordan@digium.com>2015-04-16 06:44:56 -0500
commitab6382cafd3ff13dce7916b1770b1bd61fe38f01 (patch)
treeeb238afbef1cfe8604e80750981c15f044161c5a /res
parent60d1911482c1dcf44d34e30f252857d75f5d5d77 (diff)
res_pjsip: Refactor endpt_send_request to include transaction timeout
This is the first follow-on to https://reviewboard.asterisk.org/r/4572/ and the discussion at http://lists.digium.com/pipermail/asterisk-dev/2015-March/073921.html Since we currently have no control over pjproject transaction timeout, this patch pulls the pjsip_endpt_send_request function out of pjproject and into res_pjsip/endpt_send_transaction in order to implement that capability. Now when the transaction is initiated, we also schedule our own pj_timer with our own desired timeout. If the transaction completes before either timeout, pjproject cancels its timer, and calls our tsx callback where we cancel our timer and run the app callback. If the pjproject timer times out first, pjproject calls our tsx callback where we cancel our timer and run the app callback. If our timer times out first, we terminate the transaction which causes pjproject to cancel its timer and call our tsx callback where we run the app callback. Regardless of the scenario, pjproject is calling the tsx callback inside the group_lock and there are checks in the callback to make sure it doesn't run twice. As part of this patch ast_sip_send_out_of_dialog_request was created to replace its similarly named private function. It takes a new timeout argument in milliseconds (<= 0 to disable the timeout). ASTERISK-24863 #close Reported-by: George Joseph <george.joseph@fairview5.com> Tested-by: George Joseph <george.joseph@fairview5.com> Change-Id: I0778dc730d9689c5147a444a04aee3c1026bf747
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip.c159
1 files changed, 153 insertions, 6 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index fcd8516b6..108c5b32d 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -21,6 +21,8 @@
#include <pjsip.h>
/* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */
#include <pjsip_simple.h>
+#include <pjsip/sip_transaction.h>
+#include <pj/timer.h>
#include <pjlib.h>
#include "asterisk/res_pjsip.h"
@@ -2815,6 +2817,128 @@ static pj_bool_t does_method_match(const pj_str_t *message_method, const char *s
/*! Maximum number of challenges before assuming that we are in a loop */
#define MAX_RX_CHALLENGES 10
+#define TIMER_INACTIVE 0
+#define TIMEOUT_TIMER2 5
+
+struct tsx_data {
+ void *token;
+ void (*cb)(void*, pjsip_event*);
+ pjsip_transaction *tsx;
+ pj_timer_entry *timeout_timer;
+};
+
+static void send_tsx_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event);
+
+pjsip_module send_tsx_module = {
+ .name = { "send_tsx_module", 23 },
+ .id = -1,
+ .priority = PJSIP_MOD_PRIORITY_APPLICATION,
+ .on_tsx_state = &send_tsx_on_tsx_state,
+};
+
+/*! \brief This is the pjsip_tsx_send_msg callback */
+static void send_tsx_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
+{
+ struct tsx_data *tsx_data;
+
+ if (event->type != PJSIP_EVENT_TSX_STATE) {
+ return;
+ }
+
+ tsx_data = (struct tsx_data*) tsx->mod_data[send_tsx_module.id];
+ if (tsx_data == NULL) {
+ return;
+ }
+
+ if (tsx->status_code < 200) {
+ return;
+ }
+
+ if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) {
+ ast_debug(1, "PJSIP tsx timer expired\n");
+ }
+
+ if (tsx_data->timeout_timer && tsx_data->timeout_timer->id != TIMER_INACTIVE) {
+ pj_mutex_lock(tsx->mutex_b);
+ pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(tsx->endpt),
+ tsx_data->timeout_timer, TIMER_INACTIVE);
+ pj_mutex_unlock(tsx->mutex_b);
+ }
+
+ /* Call the callback, if any, and prevent the callback from being called again
+ * by clearing the transaction's module_data.
+ */
+ tsx->mod_data[send_tsx_module.id] = NULL;
+
+ if (tsx_data->cb) {
+ (*tsx_data->cb)(tsx_data->token, event);
+ }
+}
+
+static void tsx_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+ struct tsx_data *tsx_data = entry->user_data;
+
+ entry->id = TIMER_INACTIVE;
+ ast_debug(1, "Internal tsx timer expired\n");
+ pjsip_tsx_terminate(tsx_data->tsx, PJSIP_SC_TSX_TIMEOUT);
+}
+
+static pj_status_t endpt_send_transaction(pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata, int timeout, void *token,
+ pjsip_endpt_send_callback cb)
+{
+ pjsip_transaction *tsx;
+ struct tsx_data *tsx_data;
+ pj_status_t status;
+ pjsip_event event;
+
+ ast_assert(endpt && tdata);
+
+ status = pjsip_tsx_create_uac(&send_tsx_module, tdata, &tsx);
+ if (status != PJ_SUCCESS) {
+ pjsip_tx_data_dec_ref(tdata);
+ ast_log(LOG_ERROR, "Unable to create pjsip uac\n");
+ return status;
+ }
+
+ tsx_data = PJ_POOL_ALLOC_T(tsx->pool, struct tsx_data);
+ tsx_data->token = token;
+ tsx_data->cb = cb;
+ tsx_data->tsx = tsx;
+ if (timeout > 0) {
+ tsx_data->timeout_timer = PJ_POOL_ALLOC_T(tsx->pool, pj_timer_entry);
+ } else {
+ tsx_data->timeout_timer = NULL;
+ }
+ tsx->mod_data[send_tsx_module.id] = tsx_data;
+
+ PJSIP_EVENT_INIT_TX_MSG(event, tdata);
+ pjsip_tx_data_set_transport(tdata, &tsx->tp_sel);
+
+ if (timeout > 0) {
+ pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
+
+ pj_timer_entry_init(tsx_data->timeout_timer, TIMEOUT_TIMER2,
+ tsx_data, &tsx_timer_callback);
+ pj_mutex_lock(tsx->mutex_b);
+ pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(tsx->endpt),
+ tsx_data->timeout_timer, TIMER_INACTIVE);
+ pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(tsx->endpt),
+ tsx_data->timeout_timer, &timeout_timer_val);
+ tsx_data->timeout_timer->id = TIMEOUT_TIMER2;
+ pj_mutex_unlock(tsx->mutex_b);
+ }
+
+ status = (*tsx->state_handler)(tsx, &event);
+ pjsip_tx_data_dec_ref(tdata);
+ if (status != PJ_SUCCESS) {
+ ast_log(LOG_ERROR, "Unable to send message\n");
+ return status;
+ }
+
+ return status;
+}
/*! \brief Structure to hold information about an outbound request */
struct send_request_data {
@@ -2874,7 +2998,7 @@ static void endpt_send_request_wrapper(void *token, pjsip_event *e)
}
static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
- pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb)
+ pjsip_tx_data *tdata, int timeout, void *token, pjsip_endpt_send_callback cb)
{
struct send_request_wrapper *req_wrapper;
pj_status_t ret_val;
@@ -2890,7 +3014,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
req_wrapper->callback = cb;
ao2_ref(req_wrapper, +1);
- ret_val = pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, timeout,
+ ret_val = endpt_send_transaction(ast_sip_get_pjsip_endpoint(), tdata, timeout,
req_wrapper, endpt_send_request_wrapper);
if (ret_val != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
@@ -2930,6 +3054,10 @@ static void send_request_cb(void *token, pjsip_event *e)
int res;
switch(e->body.tsx_state.type) {
+ case PJSIP_EVENT_USER:
+ /* Map USER (transaction cancelled by timeout) to TIMER */
+ e->body.tsx_state.type = PJSIP_EVENT_TIMER;
+ break;
case PJSIP_EVENT_TRANSPORT_ERROR:
case PJSIP_EVENT_TIMER:
break;
@@ -2980,8 +3108,9 @@ static void send_request_cb(void *token, pjsip_event *e)
ao2_ref(req_data, -1);
}
-static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpoint *endpoint,
- void *token, void (*callback)(void *token, pjsip_event *e))
+int ast_sip_send_out_of_dialog_request(pjsip_tx_data *tdata,
+ struct ast_sip_endpoint *endpoint, int timeout, void *token,
+ void (*callback)(void *token, pjsip_event *e))
{
struct ast_sip_supplement *supplement;
struct send_request_data *req_data;
@@ -3007,7 +3136,7 @@ static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpo
ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
ao2_cleanup(contact);
- if (endpt_send_request(endpoint, tdata, -1, req_data, send_request_cb)
+ if (endpt_send_request(endpoint, tdata, timeout, req_data, send_request_cb)
!= PJ_SUCCESS) {
ao2_cleanup(req_data);
return -1;
@@ -3025,7 +3154,7 @@ int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
if (dlg) {
return send_in_dialog_request(tdata, dlg);
} else {
- return send_out_of_dialog_request(tdata, endpoint, token, callback);
+ return ast_sip_send_out_of_dialog_request(tdata, endpoint, -1, token, callback);
}
}
@@ -3543,8 +3672,25 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
+ if (internal_sip_register_service(&send_tsx_module)) {
+ ast_log(LOG_ERROR, "Failed to initialize send request module. Aborting load\n");
+ internal_sip_unregister_service(&supplement_module);
+ ast_sip_destroy_distributor();
+ ast_res_pjsip_destroy_configuration();
+ ast_sip_destroy_global_headers();
+ stop_monitor_thread();
+ ast_sip_destroy_system();
+ pj_pool_release(memory_pool);
+ memory_pool = NULL;
+ pjsip_endpt_destroy(ast_pjsip_endpoint);
+ ast_pjsip_endpoint = NULL;
+ pj_caching_pool_destroy(&caching_pool);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
if (internal_sip_initialize_outbound_authentication()) {
ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n");
+ internal_sip_unregister_service(&send_tsx_module);
internal_sip_unregister_service(&supplement_module);
ast_sip_destroy_distributor();
ast_res_pjsip_destroy_configuration();
@@ -3588,6 +3734,7 @@ static int unload_pjsip(void *data)
ast_res_pjsip_destroy_configuration();
ast_sip_destroy_system();
ast_sip_destroy_global_headers();
+ internal_sip_unregister_service(&send_tsx_module);
internal_sip_unregister_service(&supplement_module);
if (monitor_thread) {
stop_monitor_thread();