summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/res_pjsip.h7
-rw-r--r--res/ari/resource_events.c39
-rw-r--r--res/ari/resource_events.h15
-rw-r--r--res/res_ari_events.c108
-rw-r--r--res/res_mwi_external_ami.c6
-rw-r--r--res/res_pjsip.c328
-rw-r--r--res/res_pjsip/pjsip_configuration.c17
-rw-r--r--res/res_pjsip_outbound_registration.c25
-rw-r--r--res/stasis/app.c5
-rw-r--r--rest-api-templates/ari_resource.h.mustache19
-rw-r--r--rest-api-templates/res_ari_resource.c.mustache70
11 files changed, 449 insertions, 190 deletions
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index cbae5955e..b9ece71ea 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -1304,6 +1304,13 @@ int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
*
* \retval 0 Success
* \retval -1 Failure (out-of-dialog callback will not be called.)
+ *
+ * \note Timeout processing:
+ * There are 2 timers associated with this request, PJSIP timer_b which is
+ * set globally in the "system" section of pjsip.conf, and the timeout specified
+ * on this call. The timer that expires first (before normal completion) will
+ * cause the callback to be run with e->body.tsx_state.type = PJSIP_EVENT_TIMER.
+ * The timer that expires second is simply ignored and the callback is not run again.
*/
int ast_sip_send_out_of_dialog_request(pjsip_tx_data *tdata,
struct ast_sip_endpoint *endpoint, int timeout, void *token,
diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c
index d159741c2..09bcafc2d 100644
--- a/res/ari/resource_events.c
+++ b/res/ari/resource_events.c
@@ -119,6 +119,10 @@ static void app_handler(void *data, const char *app_name,
const char *msg_application = S_OR(
ast_json_string_get(ast_json_object_get(message, "application")),
"");
+
+ if (!session) {
+ return;
+ }
/* Determine if we've been replaced */
if (strcmp(msg_type, "ApplicationReplaced") == 0 &&
@@ -168,7 +172,40 @@ static int session_register_app(struct event_session *session,
return 0;
}
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *ws_session,
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser,
+ struct ast_variable *headers,
+ struct ast_ari_events_event_websocket_args *args)
+{
+ int res = 0;
+ size_t i, j;
+
+ ast_debug(3, "/events WebSocket attempted\n");
+
+ if (args->app_count == 0) {
+ ast_http_error(ser, 400, "Bad Request", "Missing param 'app'");
+ return -1;
+ }
+
+ for (i = 0; i < args->app_count; ++i) {
+ if (ast_strlen_zero(args->app[i])) {
+ res = -1;
+ break;
+ }
+
+ res |= stasis_app_register(args->app[i], app_handler, NULL);
+ }
+
+ if (res) {
+ for (j = 0; j < i; ++j) {
+ stasis_app_unregister(args->app[j]);
+ }
+ ast_http_error(ser, 400, "Bad Request", "Invalid application provided in param 'app'.");
+ }
+
+ return res;
+}
+
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session,
struct ast_variable *headers,
struct ast_ari_events_event_websocket_args *args)
{
diff --git a/res/ari/resource_events.h b/res/ari/resource_events.h
index 646cf9bfc..2b631819b 100644
--- a/res/ari/resource_events.h
+++ b/res/ari/resource_events.h
@@ -48,6 +48,19 @@ struct ast_ari_events_event_websocket_args {
/*! Parsing context for app. */
char *app_parse;
};
+
+/*!
+ * \brief WebSocket connection for events.
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+
/*!
* \brief WebSocket connection for events.
*
@@ -55,7 +68,7 @@ struct ast_ari_events_event_websocket_args {
* \param headers HTTP headers.
* \param args Swagger parameters.
*/
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
/*! Argument struct for ast_ari_events_user_event() */
struct ast_ari_events_user_event_args {
/*! Event name */
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index 40a9dfe8c..426538511 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -53,7 +53,92 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define MAX_VALS 128
-static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+ struct ast_ari_events_event_websocket_args args = {};
+ int res = 0;
+ RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+ struct ast_variable *i;
+
+ response = ast_calloc(1, sizeof(*response));
+ if (!response) {
+ ast_log(LOG_ERROR, "Failed to create response.\n");
+ goto fin;
+ }
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "app") == 0) {
+ /* Parse comma separated list */
+ char *vals[MAX_VALS];
+ size_t j;
+
+ args.app_parse = ast_strdup(i->value);
+ if (!args.app_parse) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ if (strlen(args.app_parse) == 0) {
+ /* ast_app_separate_args can't handle "" */
+ args.app_count = 1;
+ vals[0] = args.app_parse;
+ } else {
+ args.app_count = ast_app_separate_args(
+ args.app_parse, ',', vals,
+ ARRAY_LEN(vals));
+ }
+
+ if (args.app_count == 0) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ if (args.app_count >= MAX_VALS) {
+ ast_ari_response_error(response, 400,
+ "Bad Request",
+ "Too many values for app");
+ goto fin;
+ }
+
+ args.app = ast_malloc(sizeof(*args.app) * args.app_count);
+ if (!args.app) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ for (j = 0; j < args.app_count; ++j) {
+ args.app[j] = (vals[j]);
+ }
+ } else
+ {}
+ }
+
+ res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+ if (!response) {
+ ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+ res = -1;
+ } else if (response->response_code != 0) {
+ /* Param parsing failure */
+ RAII_VAR(char *, msg, NULL, ast_json_free);
+ if (response->message) {
+ msg = ast_json_dump_string(response->message);
+ } else {
+ ast_log(LOG_ERROR, "Missing response message\n");
+ }
+
+ if (msg) {
+ ast_http_error(ser, response->response_code, response->response_text, msg);
+ }
+ res = -1;
+ }
+ ast_free(args.app_parse);
+ ast_free(args.app);
+ return res;
+}
+
+static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session,
struct ast_variable *get_params, struct ast_variable *headers)
{
struct ast_ari_events_event_websocket_args args = {};
@@ -126,16 +211,11 @@ static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_sessio
{}
}
- ast_ari_websocket_events_event_websocket(session, headers, &args);
+ ast_ari_websocket_events_event_websocket_established(session, headers, &args);
fin: __attribute__((unused))
if (response && response->response_code != 0) {
/* Param parsing failure */
- /* TODO - ideally, this would return the error code to the
- * HTTP client; but we've already done the WebSocket
- * negotiation. Param parsing should happen earlier, but we
- * need a way to pass it through the WebSocket code to the
- * callback */
RAII_VAR(char *, msg, NULL, ast_json_free);
if (response->message) {
msg = ast_json_dump_string(response->message);
@@ -351,12 +431,22 @@ static struct stasis_rest_handlers events = {
static int load_module(void)
{
int res = 0;
+ struct ast_websocket_protocol *protocol;
+
events.ws_server = ast_websocket_server_create();
if (!events.ws_server) {
return AST_MODULE_LOAD_FAILURE;
}
- res |= ast_websocket_server_add_protocol(events.ws_server,
- "ari", ast_ari_events_event_websocket_ws_cb);
+
+ protocol = ast_websocket_sub_protocol_alloc("ari");
+ if (!protocol) {
+ ao2_ref(events.ws_server, -1);
+ events.ws_server = NULL;
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
+ protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
+ res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
stasis_app_ref();
res |= ast_ari_add_handler(&events);
return res;
diff --git a/res/res_mwi_external_ami.c b/res/res_mwi_external_ami.c
index 87ce411e6..0f86173d9 100644
--- a/res/res_mwi_external_ami.c
+++ b/res/res_mwi_external_ami.c
@@ -357,9 +357,9 @@ static int load_module(void)
ast_mwi_external_ref();
res = 0;
- res |= ast_manager_register_xml_core("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
- res |= ast_manager_register_xml_core("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
- res |= ast_manager_register_xml_core("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
+ res |= ast_manager_register_xml("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
+ res |= ast_manager_register_xml("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
+ res |= ast_manager_register_xml("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
if (res) {
unload_module();
return AST_MODULE_LOAD_DECLINE;
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 5f68c4440..ff18a1d6d 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -2846,126 +2846,6 @@ static pj_bool_t does_method_match(const pj_str_t *message_method, const char *s
#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 {
/*! The endpoint associated with this request */
@@ -3010,41 +2890,212 @@ struct send_request_wrapper {
void (*callback)(void *token, pjsip_event *e);
/*! Non-zero when the callback is called. */
unsigned int cb_called;
+ /*! Timeout timer. */
+ pj_timer_entry *timeout_timer;
+ /*! Original timeout. */
+ pj_int32_t timeout;
+ /*! Timeout/cleanup lock. */
+ pj_mutex_t *lock;
+ /*! The transmit data. */
+ pjsip_tx_data *tdata;
};
-static void endpt_send_request_wrapper(void *token, pjsip_event *e)
+/*! \internal This function gets called by pjsip when the transaction ends,
+ * even if it timed out. The lock prevents a race condition if both the pjsip
+ * transaction timer and our own timer expire simultaneously.
+ */
+static void endpt_send_request_cb(void *token, pjsip_event *e)
{
struct send_request_wrapper *req_wrapper = token;
- req_wrapper->cb_called = 1;
- if (req_wrapper->callback) {
+ if (e->body.tsx_state.type == PJSIP_EVENT_TIMER) {
+ ast_debug(2, "%p: PJSIP tsx timer expired\n", req_wrapper);
+
+ if (req_wrapper->timeout_timer
+ && req_wrapper->timeout_timer->id != TIMEOUT_TIMER2) {
+ ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
+ ao2_ref(req_wrapper, -1);
+ return;
+ }
+ } else {
+ ast_debug(2, "%p: PJSIP tsx response received\n", req_wrapper);
+ }
+
+ pj_mutex_lock(req_wrapper->lock);
+
+ /* It's possible that our own timer was already processing while
+ * we were waiting on the lock so check the timer id. If it's
+ * still TIMER2 then we still need to process.
+ */
+ if (req_wrapper->timeout_timer
+ && req_wrapper->timeout_timer->id == TIMEOUT_TIMER2) {
+ int timers_cancelled = 0;
+
+ ast_debug(3, "%p: Cancelling timer\n", req_wrapper);
+
+ timers_cancelled = pj_timer_heap_cancel_if_active(
+ pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+ req_wrapper->timeout_timer, TIMER_INACTIVE);
+
+ if (timers_cancelled > 0) {
+ /* If the timer was cancelled the callback will never run so
+ * clean up its reference to the wrapper.
+ */
+ ast_debug(3, "%p: Timer cancelled\n", req_wrapper);
+ ao2_ref(req_wrapper, -1);
+ } else {
+ /* If it wasn't cancelled, it MAY be in the callback already
+ * waiting on the lock so set the id to INACTIVE so
+ * when the callback comes out of the lock, it knows to not
+ * proceed.
+ */
+ ast_debug(3, "%p: Timer already expired\n", req_wrapper);
+ req_wrapper->timeout_timer->id = TIMER_INACTIVE;
+ }
+ }
+
+ /* It's possible that our own timer expired and called the callbacks
+ * so no need to call them again.
+ */
+ if (!req_wrapper->cb_called && req_wrapper->callback) {
req_wrapper->callback(req_wrapper->token, e);
+ req_wrapper->cb_called = 1;
+ ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
}
+ pj_mutex_unlock(req_wrapper->lock);
ao2_ref(req_wrapper, -1);
}
+/*! \internal This function gets called by our own timer when it expires.
+ * If the timer is cancelled however, the function does NOT get called.
+ * The lock prevents a race condition if both the pjsip transaction timer
+ * and our own timer expire simultaneously.
+ */
+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;
+
+ ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
+ req_wrapper, req_wrapper->timeout);
+
+ pj_mutex_lock(req_wrapper->lock);
+ /* If the id is not TIMEOUT_TIMER2 then the timer was cancelled above
+ * while the lock was being held so just clean up.
+ */
+ if (entry->id != TIMEOUT_TIMER2) {
+ pj_mutex_unlock(req_wrapper->lock);
+ ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
+ ao2_ref(req_wrapper, -1);
+ return;
+ }
+
+ ast_debug(3, "%p: Timer handled here\n", req_wrapper);
+
+ PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
+ event.body.tsx_state.type = PJSIP_EVENT_TIMER;
+ entry->id = TIMER_INACTIVE;
+
+ if (!req_wrapper->cb_called && req_wrapper->callback) {
+ req_wrapper->callback(req_wrapper->token, &event);
+ req_wrapper->cb_called = 1;
+ ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
+ }
+
+ pj_mutex_unlock(req_wrapper->lock);
+ ao2_ref(req_wrapper, -1);
+}
+
+static void send_request_wrapper_destructor(void *obj)
+{
+ struct send_request_wrapper *req_wrapper = obj;
+
+ pj_mutex_destroy(req_wrapper->lock);
+ pjsip_tx_data_dec_ref(req_wrapper->tdata);
+ ast_debug(2, "%p: wrapper destroyed\n", req_wrapper);
+}
+
static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
- pjsip_tx_data *tdata, int timeout, void *token, pjsip_endpt_send_callback cb)
+ pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb)
{
struct send_request_wrapper *req_wrapper;
pj_status_t ret_val;
+ pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
/* Create wrapper to detect if the callback was actually called on an error. */
- req_wrapper = ao2_alloc_options(sizeof(*req_wrapper), NULL,
+ req_wrapper = ao2_alloc_options(sizeof(*req_wrapper), send_request_wrapper_destructor,
AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!req_wrapper) {
pjsip_tx_data_dec_ref(tdata);
return PJ_ENOMEM;
}
+
+ ast_debug(2, "%p: Wrapper created\n", req_wrapper);
+
req_wrapper->token = token;
req_wrapper->callback = cb;
+ req_wrapper->timeout = timeout;
+ req_wrapper->timeout_timer = NULL;
+ req_wrapper->lock = NULL;
+ req_wrapper->tdata = tdata;
+ /* Add a reference to tdata. The wrapper destructor cleans it up. */
+ pjsip_tx_data_add_ref(tdata);
+
+ ret_val = pj_mutex_create_simple(tdata->pool, "tsx_timeout", &req_wrapper->lock);
+ if (ret_val != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(ret_val, errmsg, sizeof(errmsg));
+ ast_log(LOG_ERROR, "Error %d '%s' sending %.*s request to endpoint %s\n",
+ (int) ret_val, errmsg, (int) pj_strlen(&tdata->msg->line.req.method.name),
+ pj_strbuf(&tdata->msg->line.req.method.name),
+ endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
+ pjsip_tx_data_dec_ref(tdata);
+ ao2_ref(req_wrapper, -1);
+ return PJ_ENOMEM;
+ }
+
+ pj_mutex_lock(req_wrapper->lock);
+
+ if (timeout > 0) {
+ pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
+
+ req_wrapper->timeout_timer = PJ_POOL_ALLOC_T(tdata->pool, pj_timer_entry);
+
+ ast_debug(2, "%p: Set timer to %d msec\n", req_wrapper, timeout);
+ pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
+ 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
+ * timer callback is executed.
+ */
+ ao2_ref(req_wrapper, +1);
+ pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
+ req_wrapper->timeout_timer, &timeout_timer_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
+ * transaction callback is executed.
+ */
ao2_ref(req_wrapper, +1);
- ret_val = endpt_send_transaction(ast_sip_get_pjsip_endpoint(), tdata, timeout,
- req_wrapper, endpt_send_request_wrapper);
+
+ ret_val = pjsip_endpt_send_request(endpt, tdata, -1, req_wrapper, endpt_send_request_cb);
if (ret_val != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
+ if (timeout > 0) {
+ pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
+ req_wrapper->timeout_timer, TIMER_INACTIVE);
+ ao2_ref(req_wrapper, -1);
+ }
+
/* Complain of failure to send the request. */
pj_strerror(ret_val, errmsg, sizeof(errmsg));
ast_log(LOG_ERROR, "Error %d '%s' sending %.*s request to endpoint %s\n",
@@ -3065,6 +3116,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
ao2_ref(req_wrapper, -1);
}
}
+ pj_mutex_unlock(req_wrapper->lock);
ao2_ref(req_wrapper, -1);
return ret_val;
}
@@ -3080,10 +3132,6 @@ 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;
@@ -3698,25 +3746,8 @@ 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();
@@ -3760,7 +3791,6 @@ 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();
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 42f16d23d..fad915f4f 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -121,9 +121,20 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
static void persistent_endpoint_contact_created_observer(const void *object)
{
- char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
+ char *id = ast_strdupa(ast_sorcery_object_get_id(object));
+ char *aor = NULL;
+ char *contact = NULL;
+
+ aor = id;
+ /* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+ if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
+ *contact = '\0';
+ contact += 2;
+ } else {
+ contact = id;
+ }
- aor = strsep(&id, ";@");
+ ast_verb(1, "Contact %s/%s has been created\n", aor, contact);
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
}
@@ -144,7 +155,7 @@ static void persistent_endpoint_contact_deleted_observer(const void *object)
contact = id;
}
- ast_verb(1, "Contact %s/%s is now Unavailable\n", aor, contact);
+ ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact);
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
}
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 6af8b8679..c910c9431 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -383,24 +383,29 @@ static int line_identify_relationship(void *obj, void *arg, int flags)
return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
}
+static struct pjsip_param *get_uri_option_line(const void *uri)
+{
+ pjsip_sip_uri *pjuri;
+ static const pj_str_t LINE_STR = { "line", 4 };
+
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+ return NULL;
+ }
+ pjuri = pjsip_uri_get_uri(uri);
+ return pjsip_param_find(&pjuri->other_param, &LINE_STR);
+}
+
/*! \brief Endpoint identifier which uses the 'line' parameter to establish a relationship to an outgoing registration */
static struct ast_sip_endpoint *line_identify(pjsip_rx_data *rdata)
{
- pjsip_sip_uri *uri;
- static const pj_str_t LINE_STR = { "line", 4 };
pjsip_param *line;
RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
- if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
- return NULL;
- }
- uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
-
- line = pjsip_param_find(&uri->other_param, &LINE_STR);
- if (!line) {
+ if (!(line = get_uri_option_line(rdata->msg_info.to->uri))
+ && !(line = get_uri_option_line(rdata->msg_info.msg->line.req.uri))) {
return NULL;
- }
+ }
states = ao2_global_obj_ref(current_states);
if (!states) {
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 330e711e6..caa27abfc 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -871,8 +871,7 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
strncpy(app->name, name, size - sizeof(*app));
app->handler = handler;
- ao2_ref(data, +1);
- app->data = data;
+ app->data = ao2_bump(data);
ao2_ref(app, +1);
return app;
@@ -950,7 +949,7 @@ void app_update(struct stasis_app *app, stasis_app_cb handler, void *data)
{
SCOPED_AO2LOCK(lock, app);
- if (app->handler) {
+ if (app->handler && app->data) {
RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
ast_verb(1, "Replacing Stasis app '%s'\n", app->name);
diff --git a/rest-api-templates/ari_resource.h.mustache b/rest-api-templates/ari_resource.h.mustache
index 3a20776a7..d3f40b6bd 100644
--- a/rest-api-templates/ari_resource.h.mustache
+++ b/rest-api-templates/ari_resource.h.mustache
@@ -89,6 +89,23 @@ int ast_ari_{{c_name}}_{{c_nickname}}_parse_body(
void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, struct ast_ari_response *response);
{{/is_req}}
{{#is_websocket}}
+
+/*!
+ * \brief {{summary}}
+{{#notes}}
+ *
+ * {{{notes}}}
+{{/notes}}
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+
/*!
* \brief {{summary}}
{{#notes}}
@@ -100,7 +117,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_
* \param headers HTTP headers.
* \param args Swagger parameters.
*/
-void ast_ari_websocket_{{c_name}}_{{c_nickname}}(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+void ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
{{/is_websocket}}
{{/operations}}
{{/apis}}
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index 4cc9e0db0..08f6204be 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -137,7 +137,52 @@ fin: __attribute__((unused))
}
{{/is_req}}
{{#is_websocket}}
-static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+ struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
+{{#has_parameters}}
+ int res = 0;
+ RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+ struct ast_variable *i;
+{{/has_parameters}}
+
+{{#has_parameters}}
+ response = ast_calloc(1, sizeof(*response));
+ if (!response) {
+ ast_log(LOG_ERROR, "Failed to create response.\n");
+ goto fin;
+ }
+{{/has_parameters}}
+
+{{> param_parsing}}
+
+ res = ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+ if (!response) {
+ ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+ res = -1;
+ } else if (response->response_code != 0) {
+ /* Param parsing failure */
+ RAII_VAR(char *, msg, NULL, ast_json_free);
+ if (response->message) {
+ msg = ast_json_dump_string(response->message);
+ } else {
+ ast_log(LOG_ERROR, "Missing response message\n");
+ }
+
+ if (msg) {
+ ast_http_error(ser, response->response_code, response->response_text, msg);
+ }
+ res = -1;
+ }
+{{> param_cleanup}}
+{{#has_parameters}}
+ return res;
+{{/has_parameters}}
+}
+
+static void ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb(struct ast_websocket *ws_session,
struct ast_variable *get_params, struct ast_variable *headers)
{
struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
@@ -175,16 +220,11 @@ static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_ses
{{> param_parsing}}
- ast_ari_websocket_{{c_name}}_{{c_nickname}}(session, headers, &args);
+ ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(session, headers, &args);
fin: __attribute__((unused))
if (response && response->response_code != 0) {
/* Param parsing failure */
- /* TODO - ideally, this would return the error code to the
- * HTTP client; but we've already done the WebSocket
- * negotiation. Param parsing should happen earlier, but we
- * need a way to pass it through the WebSocket code to the
- * callback */
RAII_VAR(char *, msg, NULL, ast_json_free);
if (response->message) {
msg = ast_json_dump_string(response->message);
@@ -211,16 +251,26 @@ static int load_module(void)
{
int res = 0;
{{#apis}}
+{{#operations}}
{{#has_websocket}}
+ struct ast_websocket_protocol *protocol;
+
{{full_name}}.ws_server = ast_websocket_server_create();
if (!{{full_name}}.ws_server) {
return AST_MODULE_LOAD_FAILURE;
}
+
+ protocol = ast_websocket_sub_protocol_alloc("{{websocket_protocol}}");
+ if (!protocol) {
+ ao2_ref({{full_name}}.ws_server, -1);
+ {{full_name}}.ws_server = NULL;
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb;
+ protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb;
{{/has_websocket}}
-{{#operations}}
{{#is_websocket}}
- res |= ast_websocket_server_add_protocol({{full_name}}.ws_server,
- "{{websocket_protocol}}", ast_ari_{{c_name}}_{{c_nickname}}_ws_cb);
+ res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol);
{{/is_websocket}}
{{/operations}}
{{/apis}}