123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- #include "websocket.h"
- #include "biglist.h"
- #include "test.h"
- #include "Debug.h"
- #include "datastring.h"
- #include "datablock.h"
- #include <cstdint>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- // Ok. Maybe 2 global variables. Shhhh. Don't tell anyone.
- websocket *the_websocket = nullptr;
- struct payload received_payload;
- websocket::websocket()
- {
- server_password = nullptr;
- chatroom_tasks = new tasks();
- next_chatclientid = 1;
- next_chatroomid = 1;
- shutdown = false;
- run_async = true; // Set to false for testing.
- }
- websocket::~websocket()
- {
- if (server_password != nullptr) {
- delete server_password;
- server_password = nullptr;
- }
- users.clear(true);
- chatrooms.clear(true);
- chatclients.clear(true);
- simplechatgames.deleteallitems();
- delete chatroom_tasks;
- chatroom_tasks = nullptr;
- }
- biglist_item<simplechatgame *> *websocket::find_simple_game(datastring gameid)
- {
- biglist_iterator<simplechatgame *>loop(&simplechatgames);
- while(!loop.eof()) {
- if (gameid == *(loop.item->gameid)) {
- return loop.row;
- }
- loop.movenext();
- }
- return nullptr;
- }
- biglist_item<simplechatgame *> *websocket::add_simple_game(simplechatgame *game)
- {
- biglist_item<simplechatgame *> *item;
- item = simplechatgames.add(game);
- if (item != nullptr) {
- game->clone();
- }
- return item;
- }
- void websocket::remove_client_from_simple_games(chatclient *client,bool senduserlistmessage)
- {
- DEBUG_FUNCTION
- simplechatgame *game;
- biglist_item<simplechatgameuser *> *loop2;
- biglist_iterator<simplechatgame *> loop1(&simplechatgames);
- DEBUG_LINE
- while (!loop1.eof()) {
- DEBUG_LINE
- game = loop1.item;
- DEBUG_LINE
- loop2 = game->getuser(client);
- DEBUG_LINE
- if (loop2 != nullptr) {
- DEBUG_LINE
- if (loop2->item->client != nullptr) {
- DEBUG_LINE
- loop2->item->client = nullptr; // Don't remove the user. Just set the client to null. They might rejoin.
- DEBUG_LINE
- if (game->usercount(true) == 0) {
- // This game has no connected users. Delete the game.
- DEBUG_LINE
- loop1.row->used = false;
- DEBUG_LINE
- idisposable::dereference((idisposable**)&game);
- DEBUG_LINE
- game = nullptr;
- } else {
- DEBUG_LINE
- game->send_user_list_to_clients();
- DEBUG_LINE
- }
- }
- }
- DEBUG_LINE
- loop1.movenext();
- }
- DEBUG_LINE
- }
- int websocket::callback_http( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
- {
- switch( reason )
- {
- case LWS_CALLBACK_HTTP:
- lws_serve_http_file( wsi, "example.html", "text/html", NULL, 0 );
- break;
- default:
- break;
- }
- return 0;
- }
- int websocket::callback_chatroom( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
- {
- const int CLOSE_CONNECTION = -1;
- Debug debug(__FILE__,__func__,__LINE__);
- chatclient *client = (chatclient *)user;
- task *task_item;
- message *new_message;
- int return_value = 0;
-
- debug = __LINE__;
- switch( reason )
- {
- case LWS_CALLBACK_ESTABLISHED:
- // (VH) after the server completes a handshake with an incoming
- // client. If you built the library with ssl support, in is a
- // pointer to the ssl struct associated with the connection or NULL.
- //printf("LWS_CALLBACK_ESTABLISHED\n");
- debug = __LINE__;
- //if (client->wsi != wsi) {
- client->initialize(wsi,the_websocket->next_chatclientid++);
- the_websocket->chatclients.add(client);
- client->send_yourchatclientid();
- //}
- debug = __LINE__;
- break;
- case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
- //printf("LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
- //datastring error;
- //error.data = (char *)in;
- //error.length = len;
- //error.println();
-
- break;
- case LWS_CALLBACK_CLIENT_ESTABLISHED:
- // after your client connection completed
- // a handshake with the remote server.
- // Your client connection is actually active
- // only when you receive LWS_CALLBACK_CLIENT_ESTABLISHED for it.
- //printf("LWS_CALLBACK_CLIENT_ESTABLISHED\n");
- debug = __LINE__;
- /*if (client->wsi != wsi) {
- printf("Initialized\n");
- client->initialize(wsi,the_websocket->next_chatclientid++);
- the_websocket->chatclients.add(client);
- client->send_yourchatclientid();
- }
- debug = __LINE__;*/
- break;
-
- case LWS_CALLBACK_CLOSED:
- // The client closed the connection.
- debug = __LINE__;
- task_item = new task();
- task_item->closeconnection(client,the_websocket->run_async);
- the_websocket->chatroom_tasks->add_task(task_item);
- task_item = nullptr;
- if (!the_websocket->run_async) {
- the_websocket->chatroom_tasks->do_tasks();
- }
- debug = __LINE__;
- break;
-
- case LWS_CALLBACK_RECEIVE:
- // The server received data from a client.
- debug = __LINE__;
- if (the_websocket->shutdown) {
- // The server is shutting down. Start closing client connections. Ignore future client requests.
- client->should_disconnect = true;
- the_websocket->remove_client_from_simple_games(client,false);
- } else if (client->wsi != nullptr) {
- new_message = new message();
- if (!new_message->set((char *)in, len)) {
- printf("Memory error.\n");
- } else {
- task_item = new task();
- task_item->receivedmessage(client,new_message,the_websocket->run_async);
- debug = __LINE__;
- the_websocket->chatroom_tasks->add_task(task_item);
- debug = __LINE__;
- if (!the_websocket->run_async) {
- the_websocket->chatroom_tasks->do_tasks();
- }
- }
- idisposable::dereference((idisposable**)&new_message);
- }
- debug = __LINE__;
- //lws_callback_on_writable_all_protocol( lws_get_context( wsi ), lws_get_protocol( wsi ) );
- break;
- case LWS_CALLBACK_SERVER_WRITEABLE:
- //printf("LWS_CALLBACK_SERVER_WRITEABLE\n");
- // Try to get a message. Send it if it's available.
- debug = __LINE__;
- if (client->wsi != nullptr) {
- new_message = client->get_next_message();
- if (new_message == nullptr) {
- if (client->should_disconnect) {
- return_value = CLOSE_CONNECTION;
- }
- } else {
- // Send the message.
- debug = __LINE__;
- lws_write( wsi, (unsigned char *)new_message->actual_message.data,new_message->actual_message.length,LWS_WRITE_TEXT);
- debug = __LINE__;
- idisposable::dereference((idisposable**)&new_message);
- // If there are more messages, send a request to send another message.
- if ((client->should_disconnect)
- || ((client->messages_to_send != nullptr)
- && (!client->messages_to_send->empty()))) {
- lws_callback_on_writable(client->wsi);
- }
- }
- }
- debug = __LINE__;
- break;
-
- default:
- break;
- }
- // When you want to close a connection, you do it by returning -1 from a callback for that connection.
- return return_value;
- }
- int websocket::callback_example( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
- {
- Debug debug(__FILE__,__func__,__LINE__);
- switch( reason )
- {
- case LWS_CALLBACK_CLIENT_ESTABLISHED:
- // after your client connection completed
- // a handshake with the remote server.
- // Your client connection is actually active
- // only when you receive LWS_CALLBACK_CLIENT_ESTABLISHED for it.
- break;
- case LWS_CALLBACK_ESTABLISHED:
- // (VH) after the server completes a handshake with an incoming
- // client. If you built the library with ssl support, in is a
- // pointer to the ssl struct associated with the connection or NULL.*/
- break;
- case LWS_CALLBACK_CLOSED:
- // when the websocket session ends
- break;
- case LWS_CALLBACK_RECEIVE:
- // data has appeared for this server endpoint from a
- // remote client, it can be found at *in and is
- // len bytes long
- debug = __LINE__;
- memcpy( &received_payload.data[LWS_SEND_BUFFER_PRE_PADDING], in, len );
- received_payload.len = len;
- lws_callback_on_writable_all_protocol( lws_get_context( wsi ), lws_get_protocol( wsi ) );
- debug = __LINE__;
- break;
- case LWS_CALLBACK_SERVER_WRITEABLE:
- // If you call lws_callback_on_writable() on a connection, you will
- // get one of these callbacks coming when the connection socket
- // is able to accept another write packet without blocking.
- // If it already was able to take another packet without blocking,
- // you'll get this callback at the next call to the service loop
- // function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
- // and servers get LWS_CALLBACK_SERVER_WRITEABLE.
- debug = __LINE__;
- lws_write( wsi, &received_payload.data[LWS_SEND_BUFFER_PRE_PADDING], received_payload.len, LWS_WRITE_TEXT );
- debug = __LINE__;
- break;
- case LWS_CALLBACK_PROTOCOL_INIT:
- // One-time call per protocol, per-vhost using it, so it can
- // do initial setup / allocations etc
- debug = __LINE__;
- break;
- default:
- break;
- }
- debug = __LINE__;
- // When you want to close a connection, you do it by returning -1 from a callback for that connection.
- return 0;
- }
- void *websocket::task_thread_routine(void *arg)
- {
- // Start of error handling.
- pid_t tid = error_signals::GetThreadID();
- error_signals *error_thread = error_signals::GetThread(tid);
- if (error_thread == nullptr)
- {
- error_thread = error_signals::AddThread(tid);
- }
- volatile int val = 0;
- if (error_thread != nullptr)
- {
- error_thread->LineNumberStack = 0;
- val = setjmp(error_thread->position);
- }
- if (val != 0)
- {
- error_thread->DisplayErrorMessage(val);
- // Exit the program.
- error_signals::RemoveHandlers();
- exit(val);
- }
- // End of error handling.
- Debug debug(__FILE__,__func__,__LINE__);
- tasks *chatroom_tasks = (tasks *)arg;
-
- while (true) { // In the future, there might be a kill command to exit this loop.
- debug = __LINE__;
- chatroom_tasks->wait_for_tasks(1);
- debug = __LINE__;
- chatroom_tasks->do_tasks();
- debug = __LINE__;
- }
- error_thread->ReleaseThread(tid); // Error thread cleanup.
- pthread_exit(NULL); // This is how threads exit.
- }
|