|
|
@ -3396,6 +3396,7 @@ struct send_request_wrapper {
|
|
|
|
static void endpt_send_request_cb(void *token, pjsip_event *e)
|
|
|
|
static void endpt_send_request_cb(void *token, pjsip_event *e)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct send_request_wrapper *req_wrapper = token;
|
|
|
|
struct send_request_wrapper *req_wrapper = token;
|
|
|
|
|
|
|
|
unsigned int cb_called;
|
|
|
|
|
|
|
|
|
|
|
|
if (e->body.tsx_state.type == PJSIP_EVENT_TIMER) {
|
|
|
|
if (e->body.tsx_state.type == PJSIP_EVENT_TIMER) {
|
|
|
|
ast_debug(2, "%p: PJSIP tsx timer expired\n", req_wrapper);
|
|
|
|
ast_debug(2, "%p: PJSIP tsx timer expired\n", req_wrapper);
|
|
|
@ -3425,7 +3426,6 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
|
|
|
|
timers_cancelled = pj_timer_heap_cancel_if_active(
|
|
|
|
timers_cancelled = pj_timer_heap_cancel_if_active(
|
|
|
|
pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
|
|
|
|
pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
|
|
|
|
req_wrapper->timeout_timer, TIMER_INACTIVE);
|
|
|
|
req_wrapper->timeout_timer, TIMER_INACTIVE);
|
|
|
|
|
|
|
|
|
|
|
|
if (timers_cancelled > 0) {
|
|
|
|
if (timers_cancelled > 0) {
|
|
|
|
/* If the timer was cancelled the callback will never run so
|
|
|
|
/* If the timer was cancelled the callback will never run so
|
|
|
|
* clean up its reference to the wrapper.
|
|
|
|
* clean up its reference to the wrapper.
|
|
|
@ -3433,25 +3433,27 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
|
|
|
|
ast_debug(3, "%p: Timer cancelled\n", req_wrapper);
|
|
|
|
ast_debug(3, "%p: Timer cancelled\n", req_wrapper);
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* If it wasn't cancelled, it MAY be in the callback already
|
|
|
|
/*
|
|
|
|
* waiting on the lock so set the id to INACTIVE so
|
|
|
|
* If it wasn't cancelled, it MAY be in the callback already
|
|
|
|
* when the callback comes out of the lock, it knows to not
|
|
|
|
* waiting on the lock. When we release the lock, it will
|
|
|
|
* proceed.
|
|
|
|
* now know not to proceed.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
ast_debug(3, "%p: Timer already expired\n", req_wrapper);
|
|
|
|
ast_debug(3, "%p: Timer already expired\n", req_wrapper);
|
|
|
|
req_wrapper->timeout_timer->id = TIMER_INACTIVE;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cb_called = req_wrapper->cb_called;
|
|
|
|
|
|
|
|
req_wrapper->cb_called = 1;
|
|
|
|
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
|
|
|
|
|
|
|
|
/* It's possible that our own timer expired and called the callbacks
|
|
|
|
/* It's possible that our own timer expired and called the callbacks
|
|
|
|
* so no need to call them again.
|
|
|
|
* so no need to call them again.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (!req_wrapper->cb_called && req_wrapper->callback) {
|
|
|
|
if (!cb_called && req_wrapper->callback) {
|
|
|
|
req_wrapper->callback(req_wrapper->token, e);
|
|
|
|
req_wrapper->callback(req_wrapper->token, e);
|
|
|
|
req_wrapper->cb_called = 1;
|
|
|
|
|
|
|
|
ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
|
|
|
|
ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -3462,15 +3464,16 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
|
|
|
|
static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
pjsip_event event;
|
|
|
|
|
|
|
|
struct send_request_wrapper *req_wrapper = entry->user_data;
|
|
|
|
struct send_request_wrapper *req_wrapper = entry->user_data;
|
|
|
|
|
|
|
|
unsigned int cb_called;
|
|
|
|
|
|
|
|
|
|
|
|
ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
|
|
|
|
ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
|
|
|
|
req_wrapper, req_wrapper->timeout);
|
|
|
|
req_wrapper, req_wrapper->timeout);
|
|
|
|
|
|
|
|
|
|
|
|
ao2_lock(req_wrapper);
|
|
|
|
ao2_lock(req_wrapper);
|
|
|
|
/* If the id is not TIMEOUT_TIMER2 then the timer was cancelled above
|
|
|
|
/*
|
|
|
|
* while the lock was being held so just clean up.
|
|
|
|
* If the id is not TIMEOUT_TIMER2 then the timer was cancelled
|
|
|
|
|
|
|
|
* before we got the lock or it was already handled so just clean up.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (entry->id != TIMEOUT_TIMER2) {
|
|
|
|
if (entry->id != TIMEOUT_TIMER2) {
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
@ -3478,20 +3481,24 @@ static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->id = TIMER_INACTIVE;
|
|
|
|
|
|
|
|
|
|
|
|
ast_debug(3, "%p: Timer handled here\n", req_wrapper);
|
|
|
|
ast_debug(3, "%p: Timer handled here\n", req_wrapper);
|
|
|
|
|
|
|
|
|
|
|
|
PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
|
|
|
|
cb_called = req_wrapper->cb_called;
|
|
|
|
event.body.tsx_state.type = PJSIP_EVENT_TIMER;
|
|
|
|
req_wrapper->cb_called = 1;
|
|
|
|
entry->id = TIMER_INACTIVE;
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!cb_called && req_wrapper->callback) {
|
|
|
|
|
|
|
|
pjsip_event event;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
|
|
|
|
|
|
|
|
event.body.tsx_state.type = PJSIP_EVENT_TIMER;
|
|
|
|
|
|
|
|
|
|
|
|
if (!req_wrapper->cb_called && req_wrapper->callback) {
|
|
|
|
|
|
|
|
req_wrapper->callback(req_wrapper->token, &event);
|
|
|
|
req_wrapper->callback(req_wrapper->token, &event);
|
|
|
|
req_wrapper->cb_called = 1;
|
|
|
|
|
|
|
|
ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
|
|
|
|
ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -3511,6 +3518,11 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
|
|
|
|
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
|
|
|
|
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
|
|
|
|
pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
|
|
|
|
pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!cb && token) {
|
|
|
|
|
|
|
|
/* Silly. Without a callback we cannot do anything with token. */
|
|
|
|
|
|
|
|
return PJ_EINVAL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Create wrapper to detect if the callback was actually called on an error. */
|
|
|
|
/* Create wrapper to detect if the callback was actually called on an error. */
|
|
|
|
req_wrapper = ao2_alloc(sizeof(*req_wrapper), send_request_wrapper_destructor);
|
|
|
|
req_wrapper = ao2_alloc(sizeof(*req_wrapper), send_request_wrapper_destructor);
|
|
|
|
if (!req_wrapper) {
|
|
|
|
if (!req_wrapper) {
|
|
|
@ -3528,7 +3540,10 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
|
|
|
|
/* Add a reference to tdata. The wrapper destructor cleans it up. */
|
|
|
|
/* Add a reference to tdata. The wrapper destructor cleans it up. */
|
|
|
|
pjsip_tx_data_add_ref(tdata);
|
|
|
|
pjsip_tx_data_add_ref(tdata);
|
|
|
|
|
|
|
|
|
|
|
|
ao2_lock(req_wrapper);
|
|
|
|
if (endpoint) {
|
|
|
|
|
|
|
|
sip_get_tpselector_from_endpoint(endpoint, &selector);
|
|
|
|
|
|
|
|
pjsip_tx_data_set_transport(tdata, &selector);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (timeout > 0) {
|
|
|
|
if (timeout > 0) {
|
|
|
|
pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
|
|
|
|
pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
|
|
|
@ -3540,9 +3555,6 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
|
|
|
|
pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
|
|
|
|
pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
|
|
|
|
req_wrapper, send_request_timer_callback);
|
|
|
|
req_wrapper, send_request_timer_callback);
|
|
|
|
|
|
|
|
|
|
|
|
pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
|
|
|
|
|
|
|
|
req_wrapper->timeout_timer, TIMER_INACTIVE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* We need to insure that the wrapper and tdata are available if/when the
|
|
|
|
/* We need to insure that the wrapper and tdata are available if/when the
|
|
|
|
* timer callback is executed.
|
|
|
|
* timer callback is executed.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -3550,7 +3562,6 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
|
|
|
|
ret_val = pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
|
|
|
|
ret_val = pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
|
|
|
|
req_wrapper->timeout_timer, &timeout_timer_val);
|
|
|
|
req_wrapper->timeout_timer, &timeout_timer_val);
|
|
|
|
if (ret_val != PJ_SUCCESS) {
|
|
|
|
if (ret_val != PJ_SUCCESS) {
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR,
|
|
|
|
ast_log(LOG_ERROR,
|
|
|
|
"Failed to set timer. Not sending %.*s request to endpoint %s.\n",
|
|
|
|
"Failed to set timer. Not sending %.*s request to endpoint %s.\n",
|
|
|
|
(int) pj_strlen(&tdata->msg->line.req.method.name),
|
|
|
|
(int) pj_strlen(&tdata->msg->line.req.method.name),
|
|
|
@ -3560,33 +3571,22 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
|
|
|
|
pjsip_tx_data_dec_ref(tdata);
|
|
|
|
pjsip_tx_data_dec_ref(tdata);
|
|
|
|
return ret_val;
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
req_wrapper->timeout_timer->id = TIMEOUT_TIMER2;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
req_wrapper->timeout_timer = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* We need to insure that the wrapper and tdata are available when the
|
|
|
|
/* We need to insure that the wrapper and tdata are available when the
|
|
|
|
* transaction callback is executed.
|
|
|
|
* transaction callback is executed.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
ao2_ref(req_wrapper, +1);
|
|
|
|
ao2_ref(req_wrapper, +1);
|
|
|
|
|
|
|
|
|
|
|
|
if (endpoint) {
|
|
|
|
|
|
|
|
sip_get_tpselector_from_endpoint(endpoint, &selector);
|
|
|
|
|
|
|
|
pjsip_tx_data_set_transport(tdata, &selector);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ret_val = pjsip_endpt_send_request(endpt, tdata, -1, req_wrapper, endpt_send_request_cb);
|
|
|
|
ret_val = pjsip_endpt_send_request(endpt, tdata, -1, req_wrapper, endpt_send_request_cb);
|
|
|
|
if (ret_val != PJ_SUCCESS) {
|
|
|
|
if (ret_val != PJ_SUCCESS) {
|
|
|
|
char errmsg[PJ_ERR_MSG_SIZE];
|
|
|
|
char errmsg[PJ_ERR_MSG_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
if (timeout > 0) {
|
|
|
|
/*
|
|
|
|
int timers_cancelled = pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
|
|
|
|
* endpt_send_request_cb is not expected to ever be called
|
|
|
|
req_wrapper->timeout_timer, TIMER_INACTIVE);
|
|
|
|
* because the request didn't get far enough to attempt
|
|
|
|
if (timers_cancelled > 0) {
|
|
|
|
* sending.
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Complain of failure to send the request. */
|
|
|
|
/* Complain of failure to send the request. */
|
|
|
|
pj_strerror(ret_val, errmsg, sizeof(errmsg));
|
|
|
|
pj_strerror(ret_val, errmsg, sizeof(errmsg));
|
|
|
@ -3595,20 +3595,37 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
|
|
|
|
pj_strbuf(&tdata->msg->line.req.method.name),
|
|
|
|
pj_strbuf(&tdata->msg->line.req.method.name),
|
|
|
|
endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
|
|
|
|
endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
|
|
|
|
|
|
|
|
|
|
|
|
/* Was the callback called? */
|
|
|
|
if (timeout > 0) {
|
|
|
|
if (req_wrapper->cb_called) {
|
|
|
|
int timers_cancelled;
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Yes so we cannot report any error. The callback
|
|
|
|
ao2_lock(req_wrapper);
|
|
|
|
* has already freed any resources associated with
|
|
|
|
timers_cancelled = pj_timer_heap_cancel_if_active(
|
|
|
|
* token.
|
|
|
|
pjsip_endpt_get_timer_heap(endpt),
|
|
|
|
*/
|
|
|
|
req_wrapper->timeout_timer, TIMER_INACTIVE);
|
|
|
|
ret_val = PJ_SUCCESS;
|
|
|
|
if (timers_cancelled > 0) {
|
|
|
|
} else {
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
/* No and it is not expected to ever be called. */
|
|
|
|
}
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
|
|
|
|
|
|
|
|
/* Was the callback called? */
|
|
|
|
|
|
|
|
if (req_wrapper->cb_called) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Yes so we cannot report any error. The callback
|
|
|
|
|
|
|
|
* has already freed any resources associated with
|
|
|
|
|
|
|
|
* token.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret_val = PJ_SUCCESS;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* No so we claim it is called so our caller can free
|
|
|
|
|
|
|
|
* any resources associated with token because of
|
|
|
|
|
|
|
|
* failure.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
req_wrapper->cb_called = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ao2_unlock(req_wrapper);
|
|
|
|
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
ao2_ref(req_wrapper, -1);
|
|
|
|
return ret_val;
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|