Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.18)

project(cppwebsockets)
find_package(PkgConfig)
pkg_check_modules(LIB_WEBSOCKETS REQUIRED libwebsockets)

set(CMAKE_CXX_FLAGS -DLWS_NO_EXTENSIONS )
include_directories(${CMAKE_INSTALL_PREFIX}/include)

add_library(cppwebsockets
Util.cpp
Util.h
WebSocketServer.cpp
WebSocketServer.h)

target_link_libraries(cppwebsockets PUBLIC
${LIB_WEBSOCKETS_LIBRARIES})

install(FILES Util.h WebSocketServer.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/cppwebsockets)
install(TARGETS cppwebsockets DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
19 changes: 9 additions & 10 deletions Util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,27 @@
* Author : Jason Kruse <jason@jasonkruse.com> or @mnisjk
* Copyright : 2014
* License : BSD (see LICENSE)
* --------------------------------------------------------------------------
* --------------------------------------------------------------------------
**/

#include <stdio.h>
#include "Util.h"
#include <cstdio>

using namespace std;
constexpr auto LOG_PREFIX = "[cppWebSockets] ";

#define LOG_PREFIX "[cppWebSockets] "

void Util::log( const string& message )
void Util::log( const std::string& message )
{
const string& logMessage = LOG_PREFIX + message;
const std::string& logMessage = LOG_PREFIX + message;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
syslog( LOG_WARNING, "%s", logMessage.c_str( ) );

#ifdef LOG_TO_STDOUT
printf( "%s\n", logMessage.c_str( ) );
#endif
std::cout << logMessage << std::endl;
#endif
}

void Util::log( const char* message )
{
log( string( message ) );
log( std::string( message ) );
}

16 changes: 7 additions & 9 deletions Util.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@
* Author : Jason Kruse <jason@jasonkruse.com> or @mnisjk
* Copyright : 2014
* License : BSD (see LICENSE)
* --------------------------------------------------------------------------
* --------------------------------------------------------------------------
**/

#ifndef _UTIL_H
#define _UTIL_H
#ifndef UTIL_H
#define UTIL_H

#include <string>
#include <syslog.h>
#include <iostream>
#include <sstream>

using namespace std;
#include <string>
#include <syslog.h>

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
Expand All @@ -27,10 +25,10 @@ using namespace std;
class Util
{
public:
static void log( const string& message );
static void log( const std::string& message );
static void log( const char* message );
template<typename T>
static inline string toString(T t) { stringstream s; s << t; return s.str(); }
static inline std::string toString(T t) { std::stringstream s; s << t; return s.str(); }
};

// Util.h
Expand Down
162 changes: 92 additions & 70 deletions WebSocketServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,30 @@
* --------------------------------------------------------------------------
**/

#include <stdlib.h>
#include <string>
#include <cstring>
#include <sys/time.h>
#include <fcntl.h>
#include "libwebsockets.h"
#include "Util.h"
#include "WebSocketServer.h"

using namespace std;
#include <cstdlib>
#include <cstring>
#include <exception>
#include <fcntl.h>
#include <string>
#include <sys/time.h>
#include <vector>

// 0 for unlimited
#define MAX_BUFFER_SIZE 0
constexpr int MAX_BUFFER_SIZE=0;

// Nasty hack because certain callbacks are statically defined
WebSocketServer *self;
static WebSocketServer *self;

static int callback_main( struct lws *wsi,
enum lws_callback_reasons reason,
void *user,
void * /* user */,
void *in,
size_t len )
{
int fd;
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + LWS_SEND_BUFFER_POST_PADDING];
unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
int fd = -1;

switch( reason ) {
case LWS_CALLBACK_ESTABLISHED:
Expand All @@ -46,22 +44,31 @@ static int callback_main( struct lws *wsi,

case LWS_CALLBACK_SERVER_WRITEABLE:
fd = lws_get_socket_fd( wsi );
while( !self->connections[fd]->buffer.empty( ) )
while( !self->connections()[fd]->buffer.empty( ) )
{
const char * message = self->connections[fd]->buffer.front( );
int msgLen = strlen(message);
int charsSent = lws_write( wsi, (unsigned char *)message, msgLen, LWS_WRITE_TEXT );
std::string message = self->connections()[fd]->buffer.front( );
size_t msgLen = message.size();

std::vector<unsigned char> buf;
buf.resize(LWS_SEND_BUFFER_PRE_PADDING + msgLen + LWS_SEND_BUFFER_POST_PADDING);
unsigned char *ptr = &buf[LWS_SEND_BUFFER_PRE_PADDING];
std::memcpy(ptr, message.c_str(), message.size());
auto charsSent = size_t(lws_write( wsi, ptr, msgLen, LWS_WRITE_TEXT ));
if( charsSent < msgLen )
self->onErrorWrapper( fd, string( "Error writing to socket" ) );
{
self->onErrorWrapper( fd, std::string( "Error writing to socket" ) );
}
else
{
// Only pop the message if it was sent successfully.
self->connections[fd]->buffer.pop_front( );
self->connections()[fd]->buffer.pop_front();
}
}
lws_callback_on_writable( wsi );
break;

case LWS_CALLBACK_RECEIVE:
self->onMessage( lws_get_socket_fd( wsi ), string( (const char *)in, len ) );
self->onMessage( lws_get_socket_fd( wsi ), std::string( reinterpret_cast<const char *>(in), len ) );
break;

case LWS_CALLBACK_CLOSED:
Expand All @@ -74,27 +81,31 @@ static int callback_main( struct lws *wsi,
return 0;
}

static struct lws_protocols protocols[] = {
{
"/",
callback_main,
0, // user data struct not used
MAX_BUFFER_SIZE,
},{ NULL, NULL, 0, 0 } // terminator
};

WebSocketServer::WebSocketServer( int port, const string certPath, const string& keyPath )
WebSocketServer::WebSocketServer( int port, const std::string &certPath, const std::string& keyPath ):
_port(port),
_keyPath(keyPath),
_certPath(certPath)
{
this->_port = port;
this->_certPath = certPath;
this->_keyPath = keyPath;
struct lws_protocols protocols[] =
{
{
"/",
callback_main,
0, // user data struct not used
MAX_BUFFER_SIZE,
0,
nullptr,
0
},
{ nullptr, nullptr, 0, 0, 0, nullptr, 0
} // terminator
};

lws_set_log_level( 0, lwsl_emit_syslog ); // We'll do our own logging, thank you.
struct lws_context_creation_info info;
memset( &info, 0, sizeof info );
struct lws_context_creation_info info = {};
info.port = this->_port;
info.iface = NULL;
info.protocols = protocols;
info.iface = nullptr;
info.protocols = &protocols[0];
#ifndef LWS_NO_EXTENSIONS
info.extensions = lws_get_internal_extensions( );
#endif
Expand All @@ -108,21 +119,23 @@ WebSocketServer::WebSocketServer( int port, const string certPath, const string&
else
{
Util::log( "Not using SSL" );
info.ssl_cert_filepath = NULL;
info.ssl_private_key_filepath = NULL;
info.ssl_cert_filepath = nullptr;
info.ssl_private_key_filepath = nullptr;
}
info.gid = -1;
info.uid = -1;
info.gid = gid_t(-1);
info.uid = gid_t(-1);
info.options = 0;

// keep alive
info.ka_time = 60; // 60 seconds until connection is suspicious
info.ka_probes = 10; // 10 probes after ^ time
info.ka_interval = 10; // 10s interval for sending probes
this->_context = lws_create_context( &info );
if( !this->_context )
throw "libwebsocket init failed";
Util::log( "Server started on port " + Util::toString( this->_port ) );
_context = lws_create_context( &info );
if(_context == nullptr)
{
throw std::invalid_argument("libwebsocket init failed");
}
Util::log( "Server started on port " + Util::toString( _port ) );

// Some of the libwebsocket stuff is define statically outside the class. This
// allows us to call instance variables from the outside. Unfortunately this
Expand All @@ -133,19 +146,19 @@ WebSocketServer::WebSocketServer( int port, const string certPath, const string&
WebSocketServer::~WebSocketServer( )
{
// Free up some memory
for( map<int,Connection*>::const_iterator it = this->connections.begin( ); it != this->connections.end( ); ++it )
for( auto it = this->connections().begin( ); it != this->connections().end( ); ++it )
{
Connection* c = it->second;
this->connections.erase( it->first );
this->connections().erase( it->first );
delete c;
}
}

void WebSocketServer::onConnectWrapper( int socketID )
{
Connection* c = new Connection;
c->createTime = time( 0 );
this->connections[ socketID ] = c;
auto *c = new Connection;
c->createTime = time( nullptr );
this->connections()[ socketID ] = c;
this->onConnect( socketID );
}

Expand All @@ -155,56 +168,65 @@ void WebSocketServer::onDisconnectWrapper( int socketID )
this->_removeConnection( socketID );
}

void WebSocketServer::onErrorWrapper( int socketID, const string& message )
void WebSocketServer::onErrorWrapper( int socketID, const std::string& message )
{
Util::log( "Error: " + message + " on socketID '" + Util::toString( socketID ) + "'" );
this->onError( socketID, message );
this->_removeConnection( socketID );
}

void WebSocketServer::send( int socketID, string data )
void WebSocketServer::send( int socketID, const std::string &data )
{
// Push this onto the buffer. It will be written out when the socket is writable.
this->connections[socketID]->buffer.push_back( data.c_str() );
connections()[socketID]->buffer.emplace_back( data.c_str() );
}

void WebSocketServer::broadcast(string data )
void WebSocketServer::broadcast(const std::string &data )
{
for( map<int,Connection*>::const_iterator it = this->connections.begin( ); it != this->connections.end( ); ++it )
this->send( it->first, data );
for( auto it = this->connections().begin( ); it != this->connections().end( ); ++it )
{
send( it->first, data );
}
}

void WebSocketServer::setValue( int socketID, const string& name, const string& value )
void WebSocketServer::setValue( int socketID, const std::string& name, const std::string& value )
{
this->connections[socketID]->keyValueMap[name] = value;
this->connections()[socketID]->keyValueMap[name] = value;
}

string WebSocketServer::getValue( int socketID, const string& name )
std::string WebSocketServer::getValue( int socketID, const std::string& name ) const
{
return this->connections[socketID]->keyValueMap[name];
auto it = _connections.find(socketID);
if (it != _connections.end())
{
return it->second->keyValueMap[name];
}
return std::string();
}
int WebSocketServer::getNumberOfConnections( )
int WebSocketServer::getNumberOfConnections( ) const
{
return this->connections.size( );
return int(_connections.size( ));
}

void WebSocketServer::run( uint64_t timeout )
void WebSocketServer::run( int timeout )
{
while( 1 )
while( true )
{
this->wait( timeout );
wait( timeout );
}
}

void WebSocketServer::wait( uint64_t timeout )
void WebSocketServer::wait( int timeout )
{
if( lws_service( this->_context, timeout ) < 0 )
throw "Error polling for socket activity.";
if( lws_service( this->_context, int(timeout) ) < 0 )
{
throw std::out_of_range("Error polling for socket activity.");
}
}

void WebSocketServer::_removeConnection( int socketID )
{
Connection* c = this->connections[ socketID ];
this->connections.erase( socketID );
Connection* c = _connections[ socketID ];
_connections.erase( socketID );
delete c;
}
Loading