|
|
@ -12,6 +12,23 @@ |
|
|
|
#include <lib/libwebsockets.h> |
|
|
|
|
|
|
|
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<libwebsocket_context, decltype(libwebsocket_context_destroy)*> context; |
|
|
|
|
|
|
@ -58,6 +76,7 @@ namespace websockets { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Basic protocol to extend, already casts the void* to the actual type
|
|
|
|
template <typename T> |
|
|
|
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 <typename T> |
|
|
|
struct TestProtocol : public Protocol<T> { |
|
|
|
std::function<int(T&)> establish_func; |
|
|
|
std::function<int(T&)> close_func; |
|
|
|
std::function<std::string(T&)> write_func; |
|
|
|
std::function<int(T&, std::string)> receive_func; |
|
|
|
struct TestProtocol { |
|
|
|
typedef T user_type; |
|
|
|
|
|
|
|
std::function<void(user_type&)> establish_func; |
|
|
|
std::function<void(user_type&)> close_func; |
|
|
|
std::function<std::string(user_type&)> write_func; |
|
|
|
std::function<void(user_type&, std::string)> 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_type*>(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);} |
|
|
|