console_server.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * Copyright (c) 2012-2015 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/taylor001/crown/blob/master/LICENSE
  4. */
  5. #include "console_server.h"
  6. #include "temp_allocator.h"
  7. #include "string_stream.h"
  8. #include "device.h"
  9. #include "lua_environment.h"
  10. #include "memory.h"
  11. #include "dynamic_string.h"
  12. #include "json.h"
  13. #include "map.h"
  14. namespace crown
  15. {
  16. ConsoleServer::ConsoleServer(uint16_t port, bool wait)
  17. {
  18. m_server.bind(port);
  19. m_server.listen(5);
  20. if (wait)
  21. {
  22. AcceptResult result;
  23. TCPSocket client;
  24. do
  25. {
  26. result = m_server.accept(client);
  27. }
  28. while (result.error != AcceptResult::NO_ERROR);
  29. add_client(client);
  30. }
  31. }
  32. void ConsoleServer::shutdown()
  33. {
  34. for (uint32_t i = 0; i < id_array::size(m_clients); i++)
  35. {
  36. m_clients[i].close();
  37. }
  38. m_server.close();
  39. }
  40. namespace console_server_internal
  41. {
  42. StringStream& sanitize(StringStream& ss, const char* msg)
  43. {
  44. using namespace string_stream;
  45. const char* ch = msg;
  46. for (; *ch; ch++)
  47. {
  48. if (*ch == '"')
  49. ss << "\\";
  50. ss << *ch;
  51. }
  52. return ss;
  53. }
  54. }
  55. void ConsoleServer::log(const char* msg, LogSeverity::Enum severity)
  56. {
  57. using namespace string_stream;
  58. using namespace console_server_internal;
  59. static const char* stt[] = { "info", "warning", "error", "debug" };
  60. // Build json message
  61. TempAllocator2048 alloc;
  62. StringStream json(alloc);
  63. json << "{\"type\":\"message\",";
  64. json << "\"severity\":\"" << stt[severity] << "\",";
  65. json << "\"message\":\""; sanitize(json, msg) << "\"}";
  66. send(c_str(json));
  67. }
  68. void ConsoleServer::send(TCPSocket client, const char* json)
  69. {
  70. uint32_t len = strlen(json);
  71. client.write((const char*)&len, 4);
  72. client.write(json, len);
  73. }
  74. void ConsoleServer::send(const char* json)
  75. {
  76. for (uint32_t i = 0; i < id_array::size(m_clients); i++)
  77. {
  78. send(m_clients[i].socket, json);
  79. }
  80. }
  81. void ConsoleServer::update()
  82. {
  83. // Check for new clients only if we have room for them
  84. if (id_array::size(m_clients) < CE_MAX_CONSOLE_CLIENTS - 1)
  85. {
  86. TCPSocket client;
  87. AcceptResult result = m_server.accept_nonblock(client);
  88. if (result.error == AcceptResult::NO_ERROR)
  89. {
  90. add_client(client);
  91. }
  92. }
  93. TempAllocator256 alloc;
  94. Array<Id> to_remove(alloc);
  95. // Update all clients
  96. for (uint32_t i = 0; i < id_array::size(m_clients); i++)
  97. {
  98. ReadResult rr = update_client(m_clients[i].socket);
  99. if (rr.error != ReadResult::NO_ERROR) array::push_back(to_remove, m_clients[i].id);
  100. }
  101. // Remove clients
  102. for (uint32_t i = 0; i < array::size(to_remove); i++)
  103. {
  104. id_array::get(m_clients, to_remove[i]).socket.close();
  105. id_array::destroy(m_clients, to_remove[i]);
  106. }
  107. }
  108. void ConsoleServer::add_client(TCPSocket socket)
  109. {
  110. Client client;
  111. client.socket = socket;
  112. Id id = id_array::create(m_clients, client);
  113. id_array::get(m_clients, id).id = id;
  114. }
  115. ReadResult ConsoleServer::update_client(TCPSocket client)
  116. {
  117. uint32_t msg_len = 0;
  118. ReadResult rr = client.read_nonblock(&msg_len, 4);
  119. // If no data received, return
  120. if (rr.error == ReadResult::NO_ERROR && rr.bytes_read == 0) return rr;
  121. if (rr.error == ReadResult::REMOTE_CLOSED) return rr;
  122. if (rr.error != ReadResult::NO_ERROR) return rr;
  123. // Else read the message
  124. Array<char> msg_buf(default_allocator());
  125. array::resize(msg_buf, msg_len);
  126. ReadResult msg_result = client.read(array::begin(msg_buf), msg_len);
  127. array::push_back(msg_buf, '\0');
  128. if (msg_result.error == ReadResult::REMOTE_CLOSED) return msg_result;
  129. if (msg_result.error != ReadResult::NO_ERROR) return msg_result;
  130. process(client, array::begin(msg_buf));
  131. return msg_result;
  132. }
  133. void ConsoleServer::process(TCPSocket client, const char* json)
  134. {
  135. Map<DynamicString, const char*> root(default_allocator());
  136. json::parse_object(json, root);
  137. DynamicString type;
  138. json::parse_string(root["type"], type);
  139. if (type == "ping") process_ping(client, json);
  140. else if (type == "script") process_script(client, json);
  141. else if (type == "command") process_command(client, json);
  142. else CE_FATAL("Request unknown.");
  143. }
  144. void ConsoleServer::process_ping(TCPSocket client, const char* /*json*/)
  145. {
  146. send(client, "{\"type\":\"pong\"}");
  147. }
  148. void ConsoleServer::process_script(TCPSocket /*client*/, const char* json)
  149. {
  150. Map<DynamicString, const char*> root(default_allocator());
  151. json::parse_object(json, root);
  152. DynamicString script;
  153. json::parse_string(root["script"], script);
  154. device()->lua_environment()->execute_string(script.c_str());
  155. }
  156. void ConsoleServer::process_command(TCPSocket /*client*/, const char* json)
  157. {
  158. Map<DynamicString, const char*> root(default_allocator());
  159. json::parse_object(json, root);
  160. DynamicString cmd;
  161. json::parse_string(root["command"], cmd);
  162. if (cmd == "reload")
  163. {
  164. DynamicString type;
  165. DynamicString name;
  166. json::parse_string(root["resource_type"], type);
  167. json::parse_string(root["resource_name"], name);
  168. device()->reload(type.c_str(), name.c_str());
  169. }
  170. else if (cmd == "pause")
  171. {
  172. device()->pause();
  173. }
  174. else if (cmd == "unpause")
  175. {
  176. device()->unpause();
  177. }
  178. }
  179. namespace console_server_globals
  180. {
  181. char _buffer[sizeof(ConsoleServer)];
  182. ConsoleServer* _console = NULL;
  183. void init(uint16_t port, bool wait)
  184. {
  185. _console = new (_buffer) ConsoleServer(port, wait);
  186. }
  187. void shutdown()
  188. {
  189. _console->~ConsoleServer();
  190. _console = NULL;
  191. }
  192. void update()
  193. {
  194. #if CROWN_DEBUG
  195. _console->update();
  196. #endif // CROWN_DEBUG
  197. }
  198. ConsoleServer& console()
  199. {
  200. return *_console;
  201. }
  202. } // namespace console_server
  203. } // namespace crown