websocket.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #include "websocket.h"
  2. #include "biglist.h"
  3. #include "test.h"
  4. #include "Debug.h"
  5. #include "datastring.h"
  6. #include "datablock.h"
  7. #include <cstdint>
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <unistd.h>
  11. // Ok. Maybe 2 global variables. Shhhh. Don't tell anyone.
  12. websocket *the_websocket = nullptr;
  13. struct payload received_payload;
  14. websocket::websocket()
  15. {
  16. server_password = nullptr;
  17. chatroom_tasks = new tasks();
  18. next_chatclientid = 1;
  19. next_chatroomid = 1;
  20. shutdown = false;
  21. run_async = true; // Set to false for testing.
  22. }
  23. websocket::~websocket()
  24. {
  25. if (server_password != nullptr) {
  26. delete server_password;
  27. server_password = nullptr;
  28. }
  29. users.clear(true);
  30. chatrooms.clear(true);
  31. chatclients.clear(true);
  32. simplechatgames.deleteallitems();
  33. delete chatroom_tasks;
  34. chatroom_tasks = nullptr;
  35. }
  36. biglist_item<simplechatgame *> *websocket::find_simple_game(datastring gameid)
  37. {
  38. biglist_iterator<simplechatgame *>loop(&simplechatgames);
  39. while(!loop.eof()) {
  40. if (gameid == *(loop.item->gameid)) {
  41. return loop.row;
  42. }
  43. loop.movenext();
  44. }
  45. return nullptr;
  46. }
  47. biglist_item<simplechatgame *> *websocket::add_simple_game(simplechatgame *game)
  48. {
  49. biglist_item<simplechatgame *> *item;
  50. item = simplechatgames.add(game);
  51. if (item != nullptr) {
  52. game->clone();
  53. }
  54. return item;
  55. }
  56. void websocket::remove_client_from_simple_games(chatclient *client,bool senduserlistmessage)
  57. {
  58. DEBUG_FUNCTION
  59. simplechatgame *game;
  60. biglist_item<simplechatgameuser *> *loop2;
  61. biglist_iterator<simplechatgame *> loop1(&simplechatgames);
  62. DEBUG_LINE
  63. while (!loop1.eof()) {
  64. DEBUG_LINE
  65. game = loop1.item;
  66. DEBUG_LINE
  67. loop2 = game->getuser(client);
  68. DEBUG_LINE
  69. if (loop2 != nullptr) {
  70. DEBUG_LINE
  71. if (loop2->item->client != nullptr) {
  72. DEBUG_LINE
  73. loop2->item->client = nullptr; // Don't remove the user. Just set the client to null. They might rejoin.
  74. DEBUG_LINE
  75. if (game->usercount(true) == 0) {
  76. // This game has no connected users. Delete the game.
  77. DEBUG_LINE
  78. loop1.row->used = false;
  79. DEBUG_LINE
  80. idisposable::dereference((idisposable**)&game);
  81. DEBUG_LINE
  82. game = nullptr;
  83. } else {
  84. DEBUG_LINE
  85. game->send_user_list_to_clients();
  86. DEBUG_LINE
  87. }
  88. }
  89. }
  90. DEBUG_LINE
  91. loop1.movenext();
  92. }
  93. DEBUG_LINE
  94. }
  95. int websocket::callback_http( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
  96. {
  97. switch( reason )
  98. {
  99. case LWS_CALLBACK_HTTP:
  100. lws_serve_http_file( wsi, "example.html", "text/html", NULL, 0 );
  101. break;
  102. default:
  103. break;
  104. }
  105. return 0;
  106. }
  107. int websocket::callback_chatroom( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
  108. {
  109. const int CLOSE_CONNECTION = -1;
  110. Debug debug(__FILE__,__func__,__LINE__);
  111. chatclient *client = (chatclient *)user;
  112. task *task_item;
  113. message *new_message;
  114. int return_value = 0;
  115. debug = __LINE__;
  116. switch( reason )
  117. {
  118. case LWS_CALLBACK_ESTABLISHED:
  119. // (VH) after the server completes a handshake with an incoming
  120. // client. If you built the library with ssl support, in is a
  121. // pointer to the ssl struct associated with the connection or NULL.
  122. //printf("LWS_CALLBACK_ESTABLISHED\n");
  123. debug = __LINE__;
  124. //if (client->wsi != wsi) {
  125. client->initialize(wsi,the_websocket->next_chatclientid++);
  126. the_websocket->chatclients.add(client);
  127. client->send_yourchatclientid();
  128. //}
  129. debug = __LINE__;
  130. break;
  131. case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
  132. //printf("LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
  133. //datastring error;
  134. //error.data = (char *)in;
  135. //error.length = len;
  136. //error.println();
  137. break;
  138. case LWS_CALLBACK_CLIENT_ESTABLISHED:
  139. // after your client connection completed
  140. // a handshake with the remote server.
  141. // Your client connection is actually active
  142. // only when you receive LWS_CALLBACK_CLIENT_ESTABLISHED for it.
  143. //printf("LWS_CALLBACK_CLIENT_ESTABLISHED\n");
  144. debug = __LINE__;
  145. /*if (client->wsi != wsi) {
  146. printf("Initialized\n");
  147. client->initialize(wsi,the_websocket->next_chatclientid++);
  148. the_websocket->chatclients.add(client);
  149. client->send_yourchatclientid();
  150. }
  151. debug = __LINE__;*/
  152. break;
  153. case LWS_CALLBACK_CLOSED:
  154. // The client closed the connection.
  155. debug = __LINE__;
  156. task_item = new task();
  157. task_item->closeconnection(client,the_websocket->run_async);
  158. the_websocket->chatroom_tasks->add_task(task_item);
  159. task_item = nullptr;
  160. if (!the_websocket->run_async) {
  161. the_websocket->chatroom_tasks->do_tasks();
  162. }
  163. debug = __LINE__;
  164. break;
  165. case LWS_CALLBACK_RECEIVE:
  166. // The server received data from a client.
  167. debug = __LINE__;
  168. if (the_websocket->shutdown) {
  169. // The server is shutting down. Start closing client connections. Ignore future client requests.
  170. client->should_disconnect = true;
  171. the_websocket->remove_client_from_simple_games(client,false);
  172. } else if (client->wsi != nullptr) {
  173. new_message = new message();
  174. if (!new_message->set((char *)in, len)) {
  175. printf("Memory error.\n");
  176. } else {
  177. task_item = new task();
  178. task_item->receivedmessage(client,new_message,the_websocket->run_async);
  179. debug = __LINE__;
  180. the_websocket->chatroom_tasks->add_task(task_item);
  181. debug = __LINE__;
  182. if (!the_websocket->run_async) {
  183. the_websocket->chatroom_tasks->do_tasks();
  184. }
  185. }
  186. idisposable::dereference((idisposable**)&new_message);
  187. }
  188. debug = __LINE__;
  189. //lws_callback_on_writable_all_protocol( lws_get_context( wsi ), lws_get_protocol( wsi ) );
  190. break;
  191. case LWS_CALLBACK_SERVER_WRITEABLE:
  192. //printf("LWS_CALLBACK_SERVER_WRITEABLE\n");
  193. // Try to get a message. Send it if it's available.
  194. debug = __LINE__;
  195. if (client->wsi != nullptr) {
  196. new_message = client->get_next_message();
  197. if (new_message == nullptr) {
  198. if (client->should_disconnect) {
  199. return_value = CLOSE_CONNECTION;
  200. }
  201. } else {
  202. // Send the message.
  203. debug = __LINE__;
  204. lws_write( wsi, (unsigned char *)new_message->actual_message.data,new_message->actual_message.length,LWS_WRITE_TEXT);
  205. debug = __LINE__;
  206. idisposable::dereference((idisposable**)&new_message);
  207. // If there are more messages, send a request to send another message.
  208. if ((client->should_disconnect)
  209. || ((client->messages_to_send != nullptr)
  210. && (!client->messages_to_send->empty()))) {
  211. lws_callback_on_writable(client->wsi);
  212. }
  213. }
  214. }
  215. debug = __LINE__;
  216. break;
  217. default:
  218. break;
  219. }
  220. // When you want to close a connection, you do it by returning -1 from a callback for that connection.
  221. return return_value;
  222. }
  223. int websocket::callback_example( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
  224. {
  225. Debug debug(__FILE__,__func__,__LINE__);
  226. switch( reason )
  227. {
  228. case LWS_CALLBACK_CLIENT_ESTABLISHED:
  229. // after your client connection completed
  230. // a handshake with the remote server.
  231. // Your client connection is actually active
  232. // only when you receive LWS_CALLBACK_CLIENT_ESTABLISHED for it.
  233. break;
  234. case LWS_CALLBACK_ESTABLISHED:
  235. // (VH) after the server completes a handshake with an incoming
  236. // client. If you built the library with ssl support, in is a
  237. // pointer to the ssl struct associated with the connection or NULL.*/
  238. break;
  239. case LWS_CALLBACK_CLOSED:
  240. // when the websocket session ends
  241. break;
  242. case LWS_CALLBACK_RECEIVE:
  243. // data has appeared for this server endpoint from a
  244. // remote client, it can be found at *in and is
  245. // len bytes long
  246. debug = __LINE__;
  247. memcpy( &received_payload.data[LWS_SEND_BUFFER_PRE_PADDING], in, len );
  248. received_payload.len = len;
  249. lws_callback_on_writable_all_protocol( lws_get_context( wsi ), lws_get_protocol( wsi ) );
  250. debug = __LINE__;
  251. break;
  252. case LWS_CALLBACK_SERVER_WRITEABLE:
  253. // If you call lws_callback_on_writable() on a connection, you will
  254. // get one of these callbacks coming when the connection socket
  255. // is able to accept another write packet without blocking.
  256. // If it already was able to take another packet without blocking,
  257. // you'll get this callback at the next call to the service loop
  258. // function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
  259. // and servers get LWS_CALLBACK_SERVER_WRITEABLE.
  260. debug = __LINE__;
  261. lws_write( wsi, &received_payload.data[LWS_SEND_BUFFER_PRE_PADDING], received_payload.len, LWS_WRITE_TEXT );
  262. debug = __LINE__;
  263. break;
  264. case LWS_CALLBACK_PROTOCOL_INIT:
  265. // One-time call per protocol, per-vhost using it, so it can
  266. // do initial setup / allocations etc
  267. debug = __LINE__;
  268. break;
  269. default:
  270. break;
  271. }
  272. debug = __LINE__;
  273. // When you want to close a connection, you do it by returning -1 from a callback for that connection.
  274. return 0;
  275. }
  276. void *websocket::task_thread_routine(void *arg)
  277. {
  278. // Start of error handling.
  279. pid_t tid = error_signals::GetThreadID();
  280. error_signals *error_thread = error_signals::GetThread(tid);
  281. if (error_thread == nullptr)
  282. {
  283. error_thread = error_signals::AddThread(tid);
  284. }
  285. volatile int val = 0;
  286. if (error_thread != nullptr)
  287. {
  288. error_thread->LineNumberStack = 0;
  289. val = setjmp(error_thread->position);
  290. }
  291. if (val != 0)
  292. {
  293. error_thread->DisplayErrorMessage(val);
  294. // Exit the program.
  295. error_signals::RemoveHandlers();
  296. exit(val);
  297. }
  298. // End of error handling.
  299. Debug debug(__FILE__,__func__,__LINE__);
  300. tasks *chatroom_tasks = (tasks *)arg;
  301. while (true) { // In the future, there might be a kill command to exit this loop.
  302. debug = __LINE__;
  303. chatroom_tasks->wait_for_tasks(1);
  304. debug = __LINE__;
  305. chatroom_tasks->do_tasks();
  306. debug = __LINE__;
  307. }
  308. error_thread->ReleaseThread(tid); // Error thread cleanup.
  309. pthread_exit(NULL); // This is how threads exit.
  310. }