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) { RAII_VAR(struct event_session *, session, NULL, session_cleanup); struct ast_json *msg; int res; size_t i; int (* register_handler)(const char *, stasis_app_cb handler, void *data); ast_debug(3, "/events WebSocket connection\n"); session = session_create(ws_session); if (!session) { ast_ari_websocket_session_write(ws_session, ast_ari_oom_json()); return; } if (args->subscribe_all) { register_handler = &stasis_app_register_all; } else { register_handler = &stasis_app_register; } res = 0; for (i = 0; i < args->app_count; ++i) { if (ast_strlen_zero(args->app[i])) { continue; } res |= session_register_app(session, args->app[i], register_handler); } if (ao2_container_count(session->websocket_apps) == 0) { RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref); msg = ast_json_pack("{s: s, s: [s]}", "type", "MissingParams", "params", "app"); if (!msg) { msg = ast_json_ref(ast_ari_oom_json()); } ast_ari_websocket_session_write(session->ws_session, msg); return; } if (res != 0) { ast_ari_websocket_session_write(ws_session, ast_ari_oom_json()); return; } /* We don't process any input, but we'll consume it waiting for EOF */ while ((msg = ast_ari_websocket_session_read(ws_session))) { ast_json_unref(msg); } }
/*! * \brief Callback handler for Stasis application messages. */ static void app_handler(void *data, const char *app_name, struct ast_json *message) { struct event_session *session = data; int res; const char *msg_type = S_OR( ast_json_string_get(ast_json_object_get(message, "type")), ""); const char *msg_application = S_OR( ast_json_string_get(ast_json_object_get(message, "application")), ""); /* Determine if we've been replaced */ if (strcmp(msg_type, "ApplicationReplaced") == 0 && strcmp(msg_application, app_name) == 0) { ao2_find(session->websocket_apps, msg_application, OBJ_UNLINK | OBJ_NODATA); } res = ast_json_object_set(message, "application", ast_json_string_create(app_name)); if(res != 0) { return; } ao2_lock(session); if (session->ws_session) { ast_ari_websocket_session_write(session->ws_session, message); } ao2_unlock(session); }
/*! * \brief Callback handler for Stasis application messages. * * \internal * * \param data Void pointer to the event session (\ref event_session). * \param app_name Name of the Stasis application that dispatched the message. * \param message The dispatched message. */ static void stasis_app_message_handler( void *data, const char *app_name, struct ast_json *message) { struct event_session *session = data; const char *msg_type, *msg_application; ast_assert(session != NULL); ao2_lock(session); msg_type = S_OR(ast_json_string_get(ast_json_object_get(message, "type")), ""); msg_application = S_OR( ast_json_string_get(ast_json_object_get(message, "application")), ""); /* If we've been replaced, remove the application from our local websocket_apps container */ if (strcmp(msg_type, "ApplicationReplaced") == 0 && strcmp(msg_application, app_name) == 0) { ao2_find(session->websocket_apps, msg_application, OBJ_UNLINK | OBJ_NODATA); } /* Now, we need to determine our state to see how we will handle the message */ if (ast_json_object_set(message, "application", ast_json_string_create(app_name))) { /* We failed to add an application element to our json message */ ast_log(LOG_WARNING, "Failed to dispatch '%s' message from Stasis app '%s'; could not update message\n", msg_type, msg_application); } else if (!session->ws_session) { /* If the websocket is NULL, the message goes to the queue */ AST_VECTOR_APPEND(&session->message_queue, message); ast_log(LOG_WARNING, "Queued '%s' message for Stasis app '%s'; websocket is not ready\n", msg_type, msg_application); } else { /* We are ready to publish the message */ ast_ari_websocket_session_write(session->ws_session, message); } ao2_unlock(session); }
/*! * \brief Updates the websocket session for an \ref event_session. * * \details The websocket for the given \ref event_session will be updated to the value * of the \c ws_session argument. * * If the value of the \c ws_session is not \c NULL and there are messages in the * event session's \c message_queue, the messages are dispatched and removed from * the queue. * * \internal * * \param session The event session object to update (\ref event_session). * \param ws_session Handle to the underlying websocket session * (\ref ast_ari_websocket_session). */ static void event_session_update_websocket( struct event_session *session, struct ast_ari_websocket_session *ws_session) { int i; ast_assert(session != NULL); ao2_lock(session); session->ws_session = ws_session; for (i = 0; i < AST_VECTOR_SIZE(&session->message_queue); i++) { struct ast_json *msg = AST_VECTOR_GET(&session->message_queue, i); ast_ari_websocket_session_write(session->ws_session, msg); ast_json_unref(msg); } AST_VECTOR_RESET(&session->message_queue, AST_VECTOR_ELEM_CLEANUP_NOOP); ao2_unlock(session); }
/*! * \brief Register for all of the apps given. * \param session Session info struct. * \param app_name Name of application to register. */ static int session_register_app(struct event_session *session, const char *app_name) { SCOPED_AO2LOCK(lock, session); ast_assert(session->ws_session != NULL); ast_assert(session->websocket_apps != NULL); if (ast_strlen_zero(app_name)) { return -1; } if (ast_str_container_add(session->websocket_apps, app_name)) { ast_ari_websocket_session_write(session->ws_session, ast_ari_oom_json()); return -1; } stasis_app_register(app_name, app_handler, session); return 0; }