You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
5.8 KiB
214 lines
5.8 KiB
|
|
#include <string>
|
|
#include <memory>
|
|
#include <functional>
|
|
#include <stdexcept>
|
|
|
|
#include <syslog.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
|
|
#include <lib/libwebsockets.h>
|
|
|
|
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 <typename... T>\
|
|
void nice_name(std::string const & format, T... args){\
|
|
lwsl_name(format.c_str(), 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<libwebsocket_context, decltype(libwebsocket_context_destroy)*> 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 <typename T>
|
|
struct Protocol {
|
|
typedef T user_type;
|
|
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<T*>(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 <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;
|
|
|
|
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){
|
|
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;
|
|
}
|
|
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)\
|
|
{ name, WSprotocol_callback(protocol), sizeof(decltype(protocol)::user_type) }
|
|
|
|
|
|
static 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;
|
|
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=<p>] "
|
|
"[-d <log bitfield>]\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
websockets::Log log("lwsts", syslog_options, debug_level);
|
|
|
|
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
|
|
|