ari_websockets.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 "internal.h"
  24. /*! \file
  25. *
  26. * \brief WebSocket support for RESTful API's.
  27. * \author David M. Lee, II <dlee@digium.com>
  28. */
  29. struct ast_ari_websocket_session {
  30. struct ast_websocket *ws_session;
  31. int (*validator)(struct ast_json *);
  32. };
  33. static void websocket_session_dtor(void *obj)
  34. {
  35. struct ast_ari_websocket_session *session = obj;
  36. ast_websocket_unref(session->ws_session);
  37. session->ws_session = NULL;
  38. }
  39. /*!
  40. * \brief Validator that always succeeds.
  41. */
  42. static int null_validator(struct ast_json *json)
  43. {
  44. return 1;
  45. }
  46. struct ast_ari_websocket_session *ast_ari_websocket_session_create(
  47. struct ast_websocket *ws_session, int (*validator)(struct ast_json *))
  48. {
  49. RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
  50. RAII_VAR(struct ast_ari_conf *, config, ast_ari_config_get(), ao2_cleanup);
  51. if (ws_session == NULL) {
  52. return NULL;
  53. }
  54. if (config == NULL || config->general == NULL) {
  55. return NULL;
  56. }
  57. if (validator == NULL) {
  58. validator = null_validator;
  59. }
  60. if (ast_websocket_set_nonblock(ws_session) != 0) {
  61. ast_log(LOG_ERROR,
  62. "ARI web socket failed to set nonblock; closing: %s\n",
  63. strerror(errno));
  64. return NULL;
  65. }
  66. if (ast_websocket_set_timeout(ws_session, config->general->write_timeout)) {
  67. ast_log(LOG_WARNING, "Failed to set write timeout %d on ARI web socket\n",
  68. config->general->write_timeout);
  69. }
  70. session = ao2_alloc(sizeof(*session), websocket_session_dtor);
  71. if (!session) {
  72. return NULL;
  73. }
  74. ao2_ref(ws_session, +1);
  75. session->ws_session = ws_session;
  76. session->validator = validator;
  77. ao2_ref(session, +1);
  78. return session;
  79. }
  80. struct ast_json *ast_ari_websocket_session_read(
  81. struct ast_ari_websocket_session *session)
  82. {
  83. RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
  84. while (!message) {
  85. int res;
  86. char *payload;
  87. uint64_t payload_len;
  88. enum ast_websocket_opcode opcode;
  89. int fragmented;
  90. res = ast_wait_for_input(
  91. ast_websocket_fd(session->ws_session), -1);
  92. if (res <= 0) {
  93. ast_log(LOG_WARNING, "WebSocket poll error: %s\n",
  94. strerror(errno));
  95. return NULL;
  96. }
  97. res = ast_websocket_read(session->ws_session, &payload,
  98. &payload_len, &opcode, &fragmented);
  99. if (res != 0) {
  100. ast_log(LOG_WARNING, "WebSocket read error: %s\n",
  101. strerror(errno));
  102. return NULL;
  103. }
  104. switch (opcode) {
  105. case AST_WEBSOCKET_OPCODE_CLOSE:
  106. ast_debug(1, "WebSocket closed by peer\n");
  107. return NULL;
  108. case AST_WEBSOCKET_OPCODE_TEXT:
  109. message = ast_json_load_buf(payload, payload_len, NULL);
  110. if (message == NULL) {
  111. ast_log(LOG_WARNING,
  112. "WebSocket input failed to parse\n");
  113. }
  114. break;
  115. default:
  116. /* Ignore all other message types */
  117. break;
  118. }
  119. }
  120. return ast_json_ref(message);
  121. }
  122. #define VALIDATION_FAILED \
  123. "{" \
  124. " \"error\": \"InvalidMessage\"," \
  125. " \"message\": \"Message validation failed\"" \
  126. "}"
  127. int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
  128. struct ast_json *message)
  129. {
  130. RAII_VAR(char *, str, NULL, ast_json_free);
  131. #ifdef AST_DEVMODE
  132. if (!session->validator(message)) {
  133. ast_log(LOG_ERROR, "Outgoing message failed validation\n");
  134. return ast_websocket_write(session->ws_session,
  135. AST_WEBSOCKET_OPCODE_TEXT, VALIDATION_FAILED,
  136. strlen(VALIDATION_FAILED));
  137. }
  138. #endif
  139. str = ast_json_dump_string_format(message, ast_ari_json_format());
  140. if (str == NULL) {
  141. ast_log(LOG_ERROR, "Failed to encode JSON object\n");
  142. return -1;
  143. }
  144. ast_debug(3, "Examining ARI event: \n%s\n", str);
  145. return ast_websocket_write(session->ws_session,
  146. AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
  147. }
  148. void ari_handle_websocket(struct ast_websocket_server *ws_server,
  149. struct ast_tcptls_session_instance *ser, const char *uri,
  150. enum ast_http_method method, struct ast_variable *get_params,
  151. struct ast_variable *headers)
  152. {
  153. struct ast_http_uri fake_urih = {
  154. .data = ws_server,
  155. };
  156. ast_websocket_uri_cb(ser, &fake_urih, uri, method, get_params,
  157. headers);
  158. }