diff --git a/CMakeLists.txt b/CMakeLists.txt index e45cdf5..a0dcc55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ cmake_minimum_required(VERSION 2.8) #join("${warnings}" " " warnings) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warnings}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") project(server) diff --git a/OnlineBeats.sublime-project b/OnlineBeats.sublime-project index 300d5b5..aabbdae 100644 --- a/OnlineBeats.sublime-project +++ b/OnlineBeats.sublime-project @@ -10,8 +10,11 @@ { "sublimeclang_options": [ + "-isystem/Users/joshua/Documents/Code/libcxx/include/", "-isystem${folder:${project_path:OnlineBeats.sublime-project}}/contrib/libwebsockets/", - "-Wall" + "-Wall", + "-std=c++11", + "-stdlib=libc++" ] } } diff --git a/main.cpp b/main.cpp index de78461..362ef7a 100644 --- a/main.cpp +++ b/main.cpp @@ -22,233 +22,45 @@ * MA 02110-1301 USA */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CMAKE_BUILD -#include "lws_config.h" -#endif +#include +#include #include +#include "websockets.h" -int force_exit = 0; - -#define MAX_ECHO_PAYLOAD 1400 +int uid = 0; +std::map people_online; -struct per_session_data__echo { - unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + MAX_ECHO_PAYLOAD + LWS_SEND_BUFFER_POST_PADDING]; - unsigned int len; +struct User { unsigned int index; - int counter; }; -void scramble(unsigned char* str, unsigned int len){ - unsigned int i = rand() % (len-1); - unsigned int j = i+1; - char t = str[i]; - str[i] = str[j]; - str[j] = t; -} - -static int -callback_echo(struct libwebsocket_context *context, - struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, void *user, - void *in, size_t len){ - struct per_session_data__echo *pss = (struct per_session_data__echo *)user; - int n; - - switch (reason) { - case LWS_CALLBACK_SERVER_WRITEABLE: - pss->counter++; - scramble(&pss->buf[LWS_SEND_BUFFER_PRE_PADDING], pss->len); - n = libwebsocket_write(wsi, &pss->buf[LWS_SEND_BUFFER_PRE_PADDING], pss->len, LWS_WRITE_TEXT); - if (n < 0) { - lwsl_err("ERROR %d writing to socket, hanging up\n", n); - return 1; - } - if (n < pss->len) { - lwsl_err("Partial write\n"); - return -1; - } - break; - - case LWS_CALLBACK_RECEIVE: - if (len > MAX_ECHO_PAYLOAD) { - lwsl_err("Server received packet bigger than %u, hanging up\n", MAX_ECHO_PAYLOAD); - return 1; - } - memcpy(&pss->buf[LWS_SEND_BUFFER_PRE_PADDING], in, len); - pss->len = len; - libwebsocket_callback_on_writable(context, wsi); - break; - case LWS_CALLBACK_CLIENT_ESTABLISHED: - case LWS_CALLBACK_CLIENT_RECEIVE: - case LWS_CALLBACK_CLIENT_WRITEABLE: - default: - break; - } - +websockets::TestProtocol default_protocol{ +[](User& user) -> int{ + user.index = uid++; + people_online[user.index] = ""; + return 0; +}, +[](User& user) -> int{ + people_online.erase(user.index); + return 0; +}, +[](User& user) -> std::string{ + std::string string_to_send = "People online: " + std::to_string(people_online.size()); + for(auto x : people_online) string_to_send += ", " + x.second; + return string_to_send; +}, +[](User& user, std::string in) -> int{ + people_online[user.index] = in; return 0; } - -static struct libwebsocket_protocols protocols[] = { - /* first protocol must always be HTTP handler */ - - { - "default", /* name */ - callback_echo, /* callback */ - sizeof(struct per_session_data__echo) /* per_session_data_size */ - }, - { - NULL, NULL, 0 /* End of list */ - } }; -void sighandler(int sig){ - force_exit = 1; -} - -static struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "debug", required_argument, NULL, 'd' }, - { "port", required_argument, NULL, 'p' }, - { "ssl", no_argument, NULL, 's' }, - { "interface", required_argument, NULL, 'i' }, -#ifndef LWS_NO_DAEMONIZE - { "daemonize", no_argument, NULL, 'D' }, -#endif - { NULL, 0, 0, 0 } +static struct libwebsocket_protocols protocols[] = { + WSstandard_protocol("default", default_protocol, User), + { NULL, NULL, 0 } // end of list }; int main(int argc, char **argv){ - int n = 0; - int port = 7681; - int use_ssl = 0; - struct libwebsocket_context *context; - int opts = 0; - char interface_name[128] = ""; - const char *interface = NULL; - int syslog_options = LOG_PID | LOG_PERROR; - int client = 0; - int listen_port; - struct lws_context_creation_info info; - - int debug_level = 7; -#ifndef LWS_NO_DAEMONIZE - int daemonize = 0; -#endif - - memset(&info, 0, sizeof info); - -#ifndef LWS_NO_SERVER - lwsl_notice("Built to support server operations\n"); -#endif - - while (n >= 0) { - n = getopt_long(argc, argv, "i:hsp:d:D" - , options, NULL); - if (n < 0) - continue; - switch (n) { -#ifndef LWS_NO_DAEMONIZE - case 'D': - daemonize = 1; - syslog_options &= ~LOG_PERROR; - break; -#endif - case 'd': - debug_level = atoi(optarg); - break; - case 's': - use_ssl = 1; /* 1 = take care about cert verification, 2 = allow anything */ - break; - case 'p': - port = atoi(optarg); - break; - case 'i': - strncpy(interface_name, optarg, sizeof interface_name); - interface_name[(sizeof interface_name) - 1] = '\0'; - interface = interface_name; - break; - case '?': - case 'h': - fprintf(stderr, "Usage: libwebsockets-test-echo " - "[--ssl] " - "[--port=

] " - "[-d ]\n"); - exit(1); - } - } - -#ifndef LWS_NO_DAEMONIZE - /* - * normally lock path would be /var/lock/lwsts or similar, to - * simplify getting started without having to take care about - * permissions or running as root, set to /tmp/.lwsts-lock - */ - if (!client && daemonize && lws_daemonize("/tmp/.lwstecho-lock")) { - fprintf(stderr, "Failed to daemonize\n"); - return 1; - } -#endif - - /* we will only try to log things according to our debug_level */ - setlogmask(LOG_UPTO (LOG_DEBUG)); - openlog("lwsts", syslog_options, LOG_DAEMON); - - /* tell the library what debug level to emit and to send it to syslog */ - lws_set_log_level(debug_level, lwsl_emit_syslog); - - lwsl_notice("libwebsockets echo test - " - "(C) Copyright 2010-2013 Andy Green - " - "licensed under LGPL2.1\n"); - -#ifndef LWS_NO_SERVER - lwsl_notice("Running in server mode\n"); - listen_port = port; -#endif - - info.port = listen_port; - info.iface = interface; - info.protocols = protocols; -#ifndef LWS_NO_EXTENSIONS - info.extensions = libwebsocket_get_internal_extensions(); -#endif - if (use_ssl && !client) { - info.ssl_cert_filepath = "libwebsockets-test-server.pem"; - info.ssl_private_key_filepath = "libwebsockets-test-server.key.pem"; - } - info.gid = -1; - info.uid = -1; - info.options = opts; - - context = libwebsocket_create_context(&info); - - if (context == NULL) { - lwsl_err("libwebsocket init failed\n"); - return -1; - } - - signal(SIGINT, sighandler); - - n = 0; - while (n >= 0 && !force_exit) { - n = libwebsocket_service(context, 10); - } - - libwebsocket_context_destroy(context); - - lwsl_notice("libwebsockets-test-echo exited cleanly\n"); - - closelog(); - - return 0; + return websockets::default_main(argc, argv, protocols); } diff --git a/websockets.h b/websockets.h new file mode 100644 index 0000000..b7e26d5 --- /dev/null +++ b/websockets.h @@ -0,0 +1,216 @@ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace websockets { + struct Log { + Log(std::string const & name, int syslog_options, int debug_level){ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog(name.c_str(), syslog_options, LOG_DAEMON); + lws_set_log_level(debug_level, lwsl_emit_syslog); + } + + #define log_function(nice_name, lwsl_name)\ + template \ + void nice_name(std::string const & format, T... args){\ + lwsl_name(format.c_str(), std::forward(args)...);\ + } + + log_function(notice, lwsl_notice) + log_function(error, lwsl_err) + log_function(warning, lwsl_warn) + log_function(debug, lwsl_debug) + + #undef log_function + + ~Log(){ + closelog(); + } + }; + + struct Context { + std::unique_ptr context; + + Context(lws_context_creation_info& info) + : context(libwebsocket_create_context(&info), &libwebsocket_context_destroy) + { + if(context == nullptr){ + lwsl_err("libwebsocket init failed\n"); + throw std::runtime_error("libwebsocket init failed"); + } + } + + libwebsocket_context const * get_raw() const { + return context.get(); + } + + libwebsocket_context * get_raw() { + return context.get(); + } + }; + + template + struct Protocol { + int callback(libwebsocket_context *context, libwebsocket *wsi, libwebsocket_callback_reasons reason, void *user, void *in, size_t len){ + return call(*context, *wsi, reason, *static_cast(user), in, len); + } + virtual int call(libwebsocket_context& context, libwebsocket& wsi, libwebsocket_callback_reasons reason, T& user, void *in, size_t len){ + return 0; + }; + }; + + template + struct TestProtocol : public Protocol { + 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) + , close_func(close_func) + , write_func(write_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 n = 0; + std::string string_to_send; + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + establish_func(user); + break; + case LWS_CALLBACK_CLOSED: + close_func(user); + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + string_to_send = write_func(user); + n = libwebsocket_write(&wsi, (unsigned char *)string_to_send.c_str(), 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; + } + break; + + case LWS_CALLBACK_RECEIVE: + receive_func(user, std::string((char*)in, len)); + libwebsocket_callback_on_writable(&context, &wsi); + break; + default: + break; + } + return 0; + } + }; + +#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);} + +#define WSstandard_protocol(name, protocol, user)\ +{ name, WSprotocol_callback(protocol), sizeof(user) } + + + static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "interface", required_argument, NULL, 'i' }, + { NULL, 0, 0, 0 } + }; + + inline int default_main(int argc, char **argv, libwebsocket_protocols* protocols){ + int n = 0; + int port = 7681; + int use_ssl = 0; + int opts = 0; + char interface_name[128] = ""; + const char *interface = NULL; + int syslog_options = LOG_PID | LOG_PERROR; + int listen_port; + struct lws_context_creation_info info; + + int debug_level = 7; + + memset(&info, 0, sizeof info); + + while (n >= 0) { + n = getopt_long(argc, argv, "i:hsp:d:D" + , options, NULL); + if (n < 0) + continue; + switch (n) { + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; /* 1 = take care about cert verification, 2 = allow anything */ + break; + case 'p': + port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + interface = interface_name; + break; + case '?': + case 'h': + fprintf(stderr, "Usage: libwebsockets-test-echo " + "[--ssl] " + "[--port=

] " + "[-d ]\n"); + exit(1); + } + } + + websockets::Log log("lwsts", syslog_options, debug_level); + + log.notice("libwebsockets echo test - " + "(C) Copyright 2010-2013 Andy Green - " + "licensed under LGPL2.1\n"); + + log.notice("Running in server mode\n"); + listen_port = port; + + info.port = listen_port; + info.iface = interface; + info.protocols = protocols; + + if (use_ssl) { + info.ssl_cert_filepath = "libwebsockets-test-server.pem"; + info.ssl_private_key_filepath = "libwebsockets-test-server.key.pem"; + } + info.gid = -1; + info.uid = -1; + info.options = opts; + + static bool force_exit = false; + signal(SIGINT, [](int){ force_exit = true; }); + + websockets::Context context(info); + + while (!force_exit) { + auto ret = libwebsocket_service(context.get_raw(), 10); + if(ret < 0) break; + } + + log.notice("libwebsockets-test-echo exited cleanly\n"); + return 0; + } + +} // namespace websockets