ari_websockets.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * David M. Lee, II <dlee@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. #include "asterisk.h"
  19. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  20. #include "asterisk/ari.h"
  21. #include "asterisk/astobj2.h"
  22. #include "asterisk/http_websocket.h"
  23. #include "asterisk/stasis_app.h"
  24. #include "internal.h"
  25. /*! \file
  26. *
  27. * \brief WebSocket support for RESTful API's.
  28. * \author David M. Lee, II <dlee@digium.com>
  29. */
  30. struct ast_ari_websocket_session {
  31. struct ast_websocket *ws_session;
  32. int (*validator)(struct ast_json *);
  33. };
  34. static void websocket_session_dtor(void *obj)
  35. {
  36. struct ast_ari_websocket_session *session = obj;
  37. ast_websocket_unref(session->ws_session);
  38. session->ws_session = NULL;
  39. }
  40. /*!
  41. * \brief Validator that always succeeds.
  42. */
  43. static int null_validator(struct ast_json *json)
  44. {
  45. return 1;
  46. }
  47. struct ast_ari_websocket_session *ast_ari_websocket_session_create(
  48. struct ast_websocket *ws_session, int (*validator)(struct ast_json *))
  49. {
  50. RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
  51. RAII_VAR(struct ast_ari_conf *, config, ast_ari_config_get(), ao2_cleanup);
  52. if (ws_session == NULL) {
  53. return NULL;
  54. }
  55. if (config == NULL || config->general == NULL) {
  56. return NULL;
  57. }
  58. if (validator == NULL) {
  59. validator = null_validator;
  60. }
  61. if (ast_websocket_set_nonblock(ws_session) != 0) {
  62. ast_log(LOG_ERROR,
  63. "ARI web socket failed to set nonblock; closing: %s\n",
  64. strerror(errno));
  65. return NULL;
  66. }
  67. if (ast_websocket_set_timeout(ws_session, config->general->write_timeout)) {
  68. ast_log(LOG_WARNING, "Failed to set write timeout %d on ARI web socket\n",
  69. config->general->write_timeout);
  70. }
  71. session = ao2_alloc(sizeof(*session), websocket_session_dtor);
  72. if (!session) {
  73. return NULL;
  74. }
  75. ao2_ref(ws_session, +1);
  76. session->ws_session = ws_session;
  77. session->validator = validator;
  78. ao2_ref(session, +1);
  79. return session;
  80. }
  81. struct ast_json *ast_ari_websocket_session_read(
  82. struct ast_ari_websocket_session *session)
  83. {
  84. RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
  85. if (ast_websocket_fd(session->ws_session) < 0) {
  86. return NULL;
  87. }
  88. while (!message) {
  89. int res;
  90. char *payload;
  91. uint64_t payload_len;
  92. enum ast_websocket_opcode opcode;
  93. int fragmented;
  94. res = ast_wait_for_input(
  95. ast_websocket_fd(session->ws_session), -1);
  96. if (res <= 0) {
  97. ast_log(LOG_WARNING, "WebSocket poll error: %s\n",
  98. strerror(errno));
  99. return NULL;
  100. }
  101. res = ast_websocket_read(session->ws_session, &payload,
  102. &payload_len, &opcode, &fragmented);
  103. if (res != 0) {
  104. ast_log(LOG_WARNING, "WebSocket read error: %s\n",
  105. strerror(errno));
  106. return NULL;
  107. }
  108. switch (opcode) {
  109. case AST_WEBSOCKET_OPCODE_CLOSE:
  110. ast_debug(1, "WebSocket closed\n");
  111. return NULL;
  112. case AST_WEBSOCKET_OPCODE_TEXT:
  113. message = ast_json_load_buf(payload, payload_len, NULL);
  114. if (message == NULL) {
  115. ast_log(LOG_WARNING,
  116. "WebSocket input failed to parse\n");
  117. }
  118. break;
  119. default:
  120. /* Ignore all other message types */
  121. break;
  122. }
  123. }
  124. return ast_json_ref(message);
  125. }
  126. #define VALIDATION_FAILED \
  127. "{" \
  128. " \"error\": \"InvalidMessage\"," \
  129. " \"message\": \"Message validation failed\"" \
  130. "}"
  131. int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
  132. struct ast_json *message)
  133. {
  134. RAII_VAR(char *, str, NULL, ast_json_free);
  135. #ifdef AST_DEVMODE
  136. if (!session->validator(message)) {
  137. ast_log(LOG_ERROR, "Outgoing message failed validation\n");
  138. return ast_websocket_write_string(session->ws_session, VALIDATION_FAILED);
  139. }
  140. #endif
  141. str = ast_json_dump_string_format(message, ast_ari_json_format());
  142. if (str == NULL) {
  143. ast_log(LOG_ERROR, "Failed to encode JSON object\n");
  144. return -1;
  145. }
  146. if (ast_websocket_write_string(session->ws_session, str)) {
  147. ast_log(LOG_NOTICE, "Problem occurred during websocket write to %s, websocket closed\n",
  148. ast_sockaddr_stringify(ast_ari_websocket_session_get_remote_addr(session)));
  149. return -1;
  150. }
  151. return 0;
  152. }
  153. struct ast_sockaddr *ast_ari_websocket_session_get_remote_addr(
  154. struct ast_ari_websocket_session *session)
  155. {
  156. return ast_websocket_remote_address(session->ws_session);
  157. }
  158. void ari_handle_websocket(struct ast_websocket_server *ws_server,
  159. struct ast_tcptls_session_instance *ser, const char *uri,
  160. enum ast_http_method method, struct ast_variable *get_params,
  161. struct ast_variable *headers)
  162. {
  163. struct ast_http_uri fake_urih = {
  164. .data = ws_server,
  165. };
  166. ast_websocket_uri_cb(ser, &fake_urih, uri, method, get_params,
  167. headers);
  168. }