Browse Source

Added some comments and exceptions to handle errors

master
Joshua Moerman 11 years ago
parent
commit
99a89ec22f
  1. 9
      main.cpp
  2. 91
      websockets.h

9
main.cpp

@ -15,15 +15,13 @@ struct User {
websockets::TestProtocol<User> default_protocol{ websockets::TestProtocol<User> default_protocol{
// connection established // connection established
[](User& user) -> int{ [](User& user){
user.index = uid++; user.index = uid++;
people_online[user.index] = ""; people_online[user.index] = "";
return 0;
}, },
// connection closed // connection closed
[](User& user) -> int{ [](User& user){
people_online.erase(user.index); people_online.erase(user.index);
return 0;
}, },
// write (will always come after receive) // write (will always come after receive)
[](User& user) -> std::string{ [](User& user) -> std::string{
@ -32,9 +30,8 @@ websockets::TestProtocol<User> default_protocol{
return string_to_send; return string_to_send;
}, },
// receive // receive
[](User& user, std::string in) -> int{ [](User& user, std::string in){
people_online[user.index] = in; people_online[user.index] = in;
return 0;
} }
}; };

91
websockets.h

@ -12,6 +12,23 @@
#include <lib/libwebsockets.h> #include <lib/libwebsockets.h>
namespace websockets { 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 { struct Log {
Log(std::string const & name, int syslog_options, int debug_level){ Log(std::string const & name, int syslog_options, int debug_level){
setlogmask(LOG_UPTO (LOG_DEBUG)); setlogmask(LOG_UPTO (LOG_DEBUG));
@ -37,6 +54,7 @@ namespace websockets {
} }
}; };
// Basic wrapper for the context pointer
struct Context { struct Context {
std::unique_ptr<libwebsocket_context, decltype(libwebsocket_context_destroy)*> 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> template <typename T>
struct Protocol { struct Protocol {
typedef T user_type; 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> template <typename T>
struct TestProtocol : public Protocol<T> { struct TestProtocol {
std::function<int(T&)> establish_func; typedef T user_type;
std::function<int(T&)> close_func;
std::function<std::string(T&)> write_func; std::function<void(user_type&)> establish_func;
std::function<int(T&, std::string)> receive_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) 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) : establish_func(establish_func)
@ -83,41 +106,43 @@ namespace websockets {
, receive_func(receive_func) , receive_func(receive_func)
{} {}
virtual int call(libwebsocket_context& context, libwebsocket& wsi, libwebsocket_callback_reasons reason, T& user, void *in, size_t len){ int callback(libwebsocket_context* context, libwebsocket* wsi, libwebsocket_callback_reasons reason, void* user_ptr, void *in, size_t len){
switch (reason) { user_type& user = *static_cast<user_type*>(user_ptr);
case LWS_CALLBACK_ESTABLISHED: try{
establish_func(user); switch (reason) {
break; case LWS_CALLBACK_ESTABLISHED:
case LWS_CALLBACK_CLOSED: establish_func(user);
close_func(user); break;
break; case LWS_CALLBACK_CLOSED:
case LWS_CALLBACK_SERVER_WRITEABLE:{ close_func(user);
std::string string_to_send = write_func(user); break;
// we need the extra bytes padding on both sides :( case LWS_CALLBACK_SERVER_WRITEABLE:{
unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING]; std::string string_to_send = write_func(user);
memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size()); // we need the extra bytes padding on both sides :(
int n = libwebsocket_write(&wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.size(), LWS_WRITE_TEXT); unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING];
if (n < 0) { memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size());
lwsl_err("ERROR %d writing to socket, hanging up\n", n); int n = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.size(), LWS_WRITE_TEXT);
return 1; if (n < 0) throw runtime_error("Could not write", 1);
} if (n < string_to_send.size()) throw runtime_error("Partial write", -1);
if (n < string_to_send.size()) { break;
lwsl_err("Partial write\n");
return -1;
} }
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: } catch(runtime_error& e){
receive_func(user, std::string((char*)in, len)); lwsl_err("Exception thrown: %s", e.what());
libwebsocket_callback_on_writable(&context, &wsi); return e.return_value;
break;
default:
break;
} }
return 0; return 0;
} }
}; };
// Some wrappers to easily create the list of protocols from structs of above kind.
#define WSprotocol_callback(protocol)\ #define WSprotocol_callback(protocol)\
[](libwebsocket_context *context, libwebsocket *wsi, libwebsocket_callback_reasons reason, void *user, void *in, size_t len)\ [](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);} {return protocol.callback(context, wsi, reason, user, in, len);}