diff options
-rw-r--r-- | include/asterisk/res_pjsip.h | 7 | ||||
-rw-r--r-- | res/ari/resource_events.c | 39 | ||||
-rw-r--r-- | res/ari/resource_events.h | 15 | ||||
-rw-r--r-- | res/res_ari_events.c | 108 | ||||
-rw-r--r-- | res/res_mwi_external_ami.c | 6 | ||||
-rw-r--r-- | res/res_pjsip.c | 328 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 17 | ||||
-rw-r--r-- | res/res_pjsip_outbound_registration.c | 25 | ||||
-rw-r--r-- | res/stasis/app.c | 5 | ||||
-rw-r--r-- | rest-api-templates/ari_resource.h.mustache | 19 | ||||
-rw-r--r-- | rest-api-templates/res_ari_resource.c.mustache | 70 |
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}} |