diff --git a/main.cpp b/main.cpp index 9e5b1b7..4f5bd1d 100644 --- a/main.cpp +++ b/main.cpp @@ -15,15 +15,13 @@ struct User { websockets::TestProtocol default_protocol{ // connection established - [](User& user) -> int{ + [](User& user){ user.index = uid++; people_online[user.index] = ""; - return 0; }, // connection closed - [](User& user) -> int{ + [](User& user){ people_online.erase(user.index); - return 0; }, // write (will always come after receive) [](User& user) -> std::string{ @@ -32,9 +30,8 @@ websockets::TestProtocol default_protocol{ return string_to_send; }, // receive - [](User& user, std::string in) -> int{ + [](User& user, std::string in){ people_online[user.index] = in; - return 0; } }; diff --git a/websockets.h b/websockets.h index 1e5774c..ee1e20b 100644 --- a/websockets.h +++ b/websockets.h @@ -12,6 +12,23 @@ #include namespace websockets { + // Will be caught in the callback and program will continue to run (but connection will be closed) + struct runtime_error : public std::runtime_error { + int return_value; + + runtime_error(const char * message, int return_value = -1) + : std::runtime_error(message) + , return_value(return_value) + {} + }; + + // Will never be caught (by us), and hence things will terminate + struct fatal_error : public std::runtime_error { + fatal_error(const char * message) + : std::runtime_error(message) + {} + }; + struct Log { Log(std::string const & name, int syslog_options, int debug_level){ setlogmask(LOG_UPTO (LOG_DEBUG)); @@ -37,6 +54,7 @@ namespace websockets { } }; + // Basic wrapper for the context pointer struct Context { std::unique_ptr context; @@ -58,6 +76,7 @@ namespace websockets { } }; + // Basic protocol to extend, already casts the void* to the actual type template struct Protocol { typedef T user_type; @@ -69,12 +88,16 @@ namespace websockets { }; }; + // 4-function Protocol, provide functions to handle establishment, closing, receiving and writing (follows receiving) + // To let the callback return something non-zero (in case of error), use websockets::runtime_error template - struct TestProtocol : public Protocol { - std::function establish_func; - std::function close_func; - std::function write_func; - std::function receive_func; + struct TestProtocol { + typedef T user_type; + + std::function establish_func; + std::function close_func; + std::function write_func; + std::function receive_func; TestProtocol(decltype(establish_func) establish_func, decltype(close_func) close_func, decltype(write_func) write_func, decltype(receive_func) receive_func) : establish_func(establish_func) @@ -83,41 +106,43 @@ namespace websockets { , receive_func(receive_func) {} - virtual int call(libwebsocket_context& context, libwebsocket& wsi, libwebsocket_callback_reasons reason, T& user, void *in, size_t len){ - switch (reason) { - case LWS_CALLBACK_ESTABLISHED: - establish_func(user); - break; - case LWS_CALLBACK_CLOSED: - close_func(user); - break; - case LWS_CALLBACK_SERVER_WRITEABLE:{ - std::string string_to_send = write_func(user); - // we need the extra bytes padding on both sides :( - unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING]; - memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size()); - int n = libwebsocket_write(&wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.size(), LWS_WRITE_TEXT); - if (n < 0) { - lwsl_err("ERROR %d writing to socket, hanging up\n", n); - return 1; - } - if (n < string_to_send.size()) { - lwsl_err("Partial write\n"); - return -1; + int callback(libwebsocket_context* context, libwebsocket* wsi, libwebsocket_callback_reasons reason, void* user_ptr, void *in, size_t len){ + user_type& user = *static_cast(user_ptr); + try{ + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + establish_func(user); + break; + case LWS_CALLBACK_CLOSED: + close_func(user); + break; + case LWS_CALLBACK_SERVER_WRITEABLE:{ + std::string string_to_send = write_func(user); + // we need the extra bytes padding on both sides :( + unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING]; + memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size()); + int n = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.size(), LWS_WRITE_TEXT); + if (n < 0) throw runtime_error("Could not write", 1); + if (n < string_to_send.size()) throw runtime_error("Partial write", -1); + break; } - break; + case LWS_CALLBACK_RECEIVE: + receive_func(user, std::string((char*)in, len)); + libwebsocket_callback_on_writable(context, wsi); + break; + default: + break; } - case LWS_CALLBACK_RECEIVE: - receive_func(user, std::string((char*)in, len)); - libwebsocket_callback_on_writable(&context, &wsi); - break; - default: - break; + } catch(runtime_error& e){ + lwsl_err("Exception thrown: %s", e.what()); + return e.return_value; } return 0; } }; + +// Some wrappers to easily create the list of protocols from structs of above kind. #define WSprotocol_callback(protocol)\ [](libwebsocket_context *context, libwebsocket *wsi, libwebsocket_callback_reasons reason, void *user, void *in, size_t len)\ {return protocol.callback(context, wsi, reason, user, in, len);}