Make API handling more extensible, and a lot of refactoring. #847 #730

This commit is contained in:
Penn, John M 047828115 2019-08-16 18:39:45 -05:00
parent ba4c86858a
commit fee9b89a07
8 changed files with 268 additions and 228 deletions

View File

@ -6,44 +6,37 @@ LIBRARY DEPENDENCIES:
#ifndef WSSESSION_HH #ifndef WSSESSION_HH
#define WSSESSION_HH #define WSSESSION_HH
#include <time.h>
#include <vector> #include <vector>
#include <string>
#include <mongoose.h> #include <mongoose.h>
#include "WSSessionVariable.hh" #include "WSSessionVariable.hh"
inline uint64_t to_nanoseconds(struct timespec* t) {
return t->tv_sec * (uint64_t)1000000000L + t->tv_nsec;
}
class WSsession { class WSsession {
public:
WSsession( struct mg_connection *nc);
void setTimeInterval(unsigned int milliseconds);
void addVariable(char* vname);
void stageValuesSynchronously();
void stageValues();
void sendValues();
void pause();
void unpause();
void clear();
void exit();
int handle_msg (std::string);
int emitError(const char* fmt, ... );
public: static int bad_ref_int ;
WSsession( struct mg_connection *nc);
void setTimeInterval(unsigned int milliseconds);
void addVariable(char* vname);
void stageValuesSynchronously();
void stageValues();
void sendValues();
void pause();
void unpause();
void clear();
void exit();
int handle_msg (const char* client_msg);
int emitError(const char* fmt, ... );
static int bad_ref_int ;
private:
WSsession() {}
REF2* make_error_ref(const char* in_name);
struct mg_connection* connection;
std::vector<WSsessionVariable*> sessionVariables;
bool cyclicSendEnabled;
double stageTime;
bool valuesStaged;
long long nextTime;
long long intervalTimeTics;
private:
WSsession() {}
REF2* make_error_ref(const char* in_name);
struct mg_connection* connection;
std::vector<WSsessionVariable*> sessionVariables;
bool cyclicSendEnabled;
double stageTime;
bool valuesStaged;
long long nextTime;
long long intervalTimeTics;
}; };
#endif #endif

View File

@ -16,20 +16,19 @@ LIBRARY DEPENDENCIES:
class WSsessionVariable { class WSsessionVariable {
public: public:
WSsessionVariable( REF2* variableType); WSsessionVariable( REF2* variableType);
~WSsessionVariable(); ~WSsessionVariable();
const char* getName(); const char* getName();
void stageValue(); void stageValue();
void writeValue( std::ostream& chkpnt_os ); void writeValue( std::ostream& chkpnt_os );
private:
private: WSsessionVariable() {}
WSsessionVariable() {} REF2 *varInfo;
REF2 *varInfo; void *address;
void *address; int size;
int size; void *stageBuffer;
void *stageBuffer; bool deref;
bool deref; };
};
#endif #endif

View File

@ -0,0 +1,14 @@
/*************************************************************************
PURPOSE: (Represent Websocket variable server connection.)
LIBRARY DEPENDENCIES:
( (../src/http_GET_handlers.o))
**************************************************************************/
#ifndef HANDLE_HTTP_GET_HANDLERS_HH
#define HANDLE_HTTP_GET_HANDLERS_HH
#include <mongoose.h>
void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm);
void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm);
#endif

View File

@ -11,27 +11,40 @@ LIBRARY DEPENDENCIES:
#include <sys/socket.h> #include <sys/socket.h>
#include <mongoose.h> #include <mongoose.h>
#include <pthread.h> #include <pthread.h>
#include <string>
#include <map>
#include "../include/WSSession.hh"
typedef struct { typedef void (*httpMethodHandler)(struct mg_connection *, struct http_message *);
struct mg_mgr mgr; /* ** mongoose */ class HTTP_Server {
struct mg_connection *nc; /* ** mongoose */ public:
const char* port; const char* port;
const char* document_root; const char* document_root;
pthread_t server_thread; /* ** */ struct mg_mgr mgr; /* ** mongoose */
bool shutting_down; struct mg_connection *listener; /* ** mongoose */
pthread_t server_thread; /* ** */
bool shutting_down;
} HTTP_Server ; std::map< std::string, httpMethodHandler> httpMethodHandlerMap; /* ** */
pthread_mutex_t APIMapLock; /* ** */
#ifdef __cplusplus std::map<mg_connection*, WSsession*> sessionMap; /* ** */
extern "C" { pthread_mutex_t sessionMapLock; /* ** */
#endif struct mg_serve_http_opts http_server_options; /* ** mongoose*/
int http_default_data(HTTP_Server * S) ; struct mg_bind_opts bind_opts; /* ** mongoose*/
int http_init(HTTP_Server * S) ; pthread_cond_t serviceConnections; /* ** */
int http_top_of_frame(HTTP_Server * S) ;
int http_shutdown(HTTP_Server * S) ;
#ifdef __cplusplus
}
#endif
// Trick Job-functions
int http_default_data();
int http_init();
int http_top_of_frame();
int http_shutdown();
void sendSessionValues(struct mg_connection *nc);
void handleClientMessage(struct mg_connection *nc, std::string msg);
void addSession(struct mg_connection *nc, WSsession* session);
void deleteSession(struct mg_connection *nc);
void install_API_GET_handler(std::string APIname, httpMethodHandler handler);
void handle_API_GET_request(struct mg_connection *nc, http_message *hm, std::string handlerName);
};
#endif #endif

View File

@ -9,13 +9,13 @@ LIBRARY DEPENDENCIES:
class HttpSimObject : public Trick::SimObject { class HttpSimObject : public Trick::SimObject {
public: public:
HTTP_Server http_server ; HTTP_Server server ;
HttpSimObject() { HttpSimObject() {
("default_data") http_default_data( &http_server ) ; ("default_data") server.http_default_data() ;
("initialization") http_init( &http_server ) ; ("initialization") server.http_init() ;
("top_of_frame") http_top_of_frame( &http_server ) ; ("top_of_frame") server.http_top_of_frame() ;
("shutdown") http_shutdown( &http_server ) ; ("shutdown") server.http_shutdown() ;
} }
}; };

View File

@ -3,12 +3,12 @@ PURPOSE: (Represent the state and initial conditions of an http server)
LIBRARY DEPENDENCIES: LIBRARY DEPENDENCIES:
((simpleJSON.o)) ((simpleJSON.o))
**************************************************************************/ **************************************************************************/
#include <sstream>
#include <iomanip> // for setprecision
#include "trick/memorymanager_c_intf.h" #include "trick/memorymanager_c_intf.h"
#include "trick/exec_proto.h" #include "trick/exec_proto.h"
#include "../include/WSSession.hh" #include "../include/WSSession.hh"
#include "../include/simpleJSON.hh" #include "../include/simpleJSON.hh"
#include <sstream>
#include <iomanip> // for setprecision
WSsession::WSsession( struct mg_connection *nc ) { WSsession::WSsession( struct mg_connection *nc ) {
connection = nc; connection = nc;
@ -141,10 +141,10 @@ void WSsession::clear() {
void WSsession::exit() {} void WSsession::exit() {}
int WSsession::handle_msg (const char* client_msg) { int WSsession::handle_msg (std::string client_msg) {
int status = 0; int status = 0;
std::vector<Member*> members = parseJSON(client_msg); std::vector<Member*> members = parseJSON(client_msg.c_str());
std::vector<Member*>::iterator it; std::vector<Member*>::iterator it;
const char *cmd; const char *cmd;
const char *var_name; const char *var_name;

View File

@ -0,0 +1,65 @@
/*************************************************************************
PURPOSE: ( HTTP-GET-method-handlers )
LIBRARY DEPENDENCIES:
( (../src/http_GET_handlers.o))
**************************************************************************/
#include <sstream>
#include "../include/http_GET_handlers.hh"
#include "trick/VariableServer.hh"
extern Trick::VariableServer * the_vs ;
#include "trick/MemoryManager.hh"
extern Trick::MemoryManager* trick_MM;
// In the Trick HTTP Server, a HTTP GET request whose URI starts with the API_PREFIX
// is processed by a http-handler-function of the following form:
//
// void HTTP_METHOD_HANDLER( struct mg_connection *, struct http_message *);
//
// The purpose of these functions are generally to produce dynamically generated
// HTTP responses, like JSON. These handler-functions are installed into the HTTP_Server
// with the member-function <HTTP_Server-object>.install_API_GET_handler. For example:
//
// http.server.install_API_GET_handler("vs_connections", &handle_HTTP_GET_vs_connections);
//
// installs the function handle_HTTP_GET_vs_connections() with the key "vs_connections".
// So if, for example the host and port of the webserver is "localhost:8888", and the API_PREFIX is "/api/v1/",
// then loading the URL "localhost:8888/api/v1/vs_connections" in your browser will cause
// handle_HTTP_GET_vs_connections() to run and return its response, which in this case is a JSON object
// describing the variable server connections.
// Send a JSON object to the given mongoose HTTP connection that describes the
// Variable Server Connections.
void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm) {
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
std::stringstream ss;
ss << *the_vs << std::endl;
std::string someJSON = ss.str();
mg_printf_http_chunk(nc, "%s", someJSON.c_str());
mg_send_http_chunk(nc, "", 0);
}
static int getIntegerQueryValue(struct http_message *hm, const char* key, int defaultVal) {
char value_text[100];
if ( mg_get_http_var(&(hm->query_string), key, value_text, sizeof(value_text)) > 0) {
return atoi(value_text);
} else {
return defaultVal;
}
}
// Send a JSON object to the given mongoose HTTP connection that contains information
// about a range of memory allocations in the Trick Memory Manager.
void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm) {
int start = getIntegerQueryValue(hm, "start", 0);
int count = getIntegerQueryValue(hm, "count", 10);
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
std::stringstream ss;
trick_MM->write_JSON_alloc_list(ss, start, count);
std::string someJSON = ss.str();
mg_printf_http_chunk(nc, "%s", someJSON.c_str());
mg_send_http_chunk(nc, "", 0);
}

View File

@ -4,6 +4,7 @@ LIBRARY DEPENDENCIES:
( (
(WSSession.o) (WSSession.o)
(WSSessionVariable.o) (WSSessionVariable.o)
(http_GET_handlers.o)
) )
**************************************************************************/ **************************************************************************/
@ -33,158 +34,58 @@ Messages sent from Server to Client
} }
*/ */
#include <sstream>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "../include/http_server.h" #include "../include/http_server.h"
#include "../include/WSSession.hh" #include "../include/WSSession.hh"
#include "trick/exec_proto.h" #include "../include/http_GET_handlers.hh"
#include "trick/VariableServer.hh"
extern Trick::VariableServer * the_vs ;
#include "trick/MemoryManager.hh"
extern Trick::MemoryManager* trick_MM;
static const struct mg_str s_get_method = MG_MK_STR("GET"); static const struct mg_str s_get_method = MG_MK_STR("GET");
static const struct mg_str s_put_method = MG_MK_STR("PUT"); static const struct mg_str s_put_method = MG_MK_STR("PUT");
static const struct mg_str s_delete_method = MG_MK_STR("DELETE"); static const struct mg_str s_delete_method = MG_MK_STR("DELETE");
static const struct mg_str api_prefix = MG_MK_STR("/api/v1"); static const struct mg_str api_prefix = MG_MK_STR("/api/v1/");
// ============================================================================
// HTTP GET Handlers
// ============================================================================
// Respond to HTTP GET method with URI="/api/v1/vs_connections".
void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm) {
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
std::stringstream ss;
ss << *the_vs << std::endl;
std::string tmp = ss.str();
mg_printf_http_chunk(nc, "%s", tmp.c_str());
mg_send_http_chunk(nc, "", 0);
}
int getIntegerQueryValue(struct http_message *hm, const char* key, int defaultVal) {
char value_text[100];
if ( mg_get_http_var(&(hm->query_string), key, value_text, sizeof(value_text)) > 0) {
return atoi(value_text);
} else {
return defaultVal;
}
}
// Respond to HTTP GET method with URI="/api/v1/alloc_info".
void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm) {
int start = getIntegerQueryValue(hm, "start", 0);
int count = getIntegerQueryValue(hm, "count", 10);
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
std::stringstream ss;
trick_MM->write_JSON_alloc_list(ss, start, count);
std::string tmp = ss.str();
mg_printf_http_chunk(nc, "%s", tmp.c_str());
mg_send_http_chunk(nc, "", 0);
}
#define DEBUG
static struct mg_serve_http_opts http_server_options;
std::map<mg_connection*, WSsession*> sessionMap;
pthread_mutex_t sessionMapLock;
pthread_cond_t serviceConnections;
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
http_message *hm = (struct http_message *)ev_data; http_message *hm = (struct http_message *)ev_data;
HTTP_Server* hs = (HTTP_Server *)nc->user_data;
switch(ev) { switch(ev) {
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
std::string uri(hm->uri.p, hm->uri.len);
#ifdef DEBUG std::cout << "WEBSOCKET[" << (void*)nc << "] OPENED. URI=\"" << uri << "\"." << std::endl;
char * s = strndup(hm->uri.p, hm->uri.len);
printf("WEBSOCKET[%p] OPENED. URI=\"%s\"\n", nc, s);
free(s);
#endif
// Create a session object to store information about this web-socket connection. // Create a session object to store information about this web-socket connection.
WSsession* session = new WSsession(nc); WSsession* session = new WSsession(nc);
hs->addSession(nc, session);
pthread_mutex_lock(&sessionMapLock);
sessionMap.insert( std::pair<mg_connection*, WSsession*>(nc, session) );
pthread_mutex_unlock(&sessionMapLock);
} break; } break;
case MG_EV_WEBSOCKET_FRAME: { case MG_EV_WEBSOCKET_FRAME: { // Process websocket messages from the client (web browser).
// --------------------------------------------------------
// Process websocket messages from the client (web browser).
// --------------------------------------------------------
struct websocket_message *wm = (struct websocket_message *) ev_data; struct websocket_message *wm = (struct websocket_message *) ev_data;
char* msg = strndup((char*)wm->data, wm->size); std::string msg ((char*)wm->data, wm->size);
std::cout << "WEBSOCKET[" << (void*)nc << "] RECIEVED: " << msg << std::endl;
#ifdef DEBUG
printf("WEBSOCKET[%p] RECIEVED: %s\n", nc, msg);
#endif
if (nc->flags & MG_F_IS_WEBSOCKET) { if (nc->flags & MG_F_IS_WEBSOCKET) {
// Find the session that goes with this connection. hs->handleClientMessage(nc, msg);
std::map<mg_connection*, WSsession*>::iterator iter;
iter = sessionMap.find(nc);
if (iter != sessionMap.end()) {
WSsession* session = iter->second;
session->handle_msg(msg);
}
} }
free(msg);
} break; } break;
case MG_EV_CLOSE: { case MG_EV_CLOSE: {
if (nc->flags & MG_F_IS_WEBSOCKET) { if (nc->flags & MG_F_IS_WEBSOCKET) {
std::map<mg_connection*, WSsession*>::iterator iter; hs->deleteSession(nc);
iter = sessionMap.find(nc); std::cout << "WEBSOCKET[" << (void*)nc << "] CLOSED." << std::endl;
if (iter != sessionMap.end()) {
WSsession* session = iter->second;
delete session;
sessionMap.erase(iter);
}
#ifdef DEBUG
printf("WEBSOCKET[%p] CLOSED.\n", nc);
#endif
} }
} break; } break;
case MG_EV_POLL: { case MG_EV_POLL: {
// The MG_EV_POLL event is sent to all connections for each invocation of mg_mgr_poll(). // The MG_EV_POLL event is sent to all connections for each invocation of mg_mgr_poll(),
// The threaded function connectionAttendant() [below] periodically calls mg_mgr_poll(). // called periodically by the threaded function connectionAttendant() [below].
// Send websocket messages to the client (web browser). // Send websocket messages to the client (web browser).
if (nc->flags & MG_F_IS_WEBSOCKET) { if (nc->flags & MG_F_IS_WEBSOCKET) {
hs->sendSessionValues(nc);
// Find the session that goes with the given websocket connection,
// and tell it to send its values to the client (web browser).
std::map<mg_connection*, WSsession*>::iterator iter;
iter = sessionMap.find(nc);
if (iter != sessionMap.end()) {
WSsession* session = iter->second;
session->sendValues();
}
} }
} break; } break;
case MG_EV_HTTP_REQUEST: { case MG_EV_HTTP_REQUEST: {
std::string uri(hm->uri.p, hm->uri.len);
#ifdef DEBUG std::cout << "HTTP_REQUEST: URI = \"" << uri << "\"" << std::endl;
char * s = strndup(hm->uri.p, hm->uri.len);
printf("HTTP_REQUEST: URI = \"%s\"\n", s);
free(s);
#endif
if (mg_str_starts_with(hm->uri, api_prefix)) { if (mg_str_starts_with(hm->uri, api_prefix)) {
struct mg_str key;
key.p = hm->uri.p + api_prefix.len;
key.len = hm->uri.len - api_prefix.len;
if (mg_strcmp(hm->method, s_get_method)==0) { if (mg_strcmp(hm->method, s_get_method)==0) {
if (mg_vcmp(&key, "/vs_connections") == 0) { std::string handlerName (hm->uri.p + api_prefix.len, hm->uri.len - api_prefix.len);
handle_HTTP_GET_vs_connections(nc, hm); hs->handle_API_GET_request(nc, hm, handlerName);
} else if (mg_vcmp(&key, "/alloc_info") == 0) {
handle_HTTP_GET_alloc_info(nc, hm);
} else {
mg_http_send_error(nc, 404, "No such API.");
}
} else if (mg_strcmp(hm->method, s_put_method)==0) { } else if (mg_strcmp(hm->method, s_put_method)==0) {
mg_http_send_error(nc, 405, "PUT method not allowed."); mg_http_send_error(nc, 405, "PUT method not allowed.");
} else if (mg_strcmp(hm->method, s_delete_method)==0) { } else if (mg_strcmp(hm->method, s_delete_method)==0) {
@ -192,7 +93,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
} }
} else { } else {
// Serve the files in the document-root directory, as specified by the URI. // Serve the files in the document-root directory, as specified by the URI.
mg_serve_http(nc, (struct http_message *) ev_data, http_server_options); mg_serve_http(nc, (struct http_message *) ev_data, hs->http_server_options);
} }
} break; } break;
default: { default: {
@ -203,62 +104,118 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
// ========================================================================= // =========================================================================
// This function runs in its own pthread to operate the webserver. // This function runs in its own pthread to operate the webserver.
// ========================================================================= // =========================================================================
void* connectionAttendant (void* arg) { static void* connectionAttendant (void* arg) {
HTTP_Server *S = (HTTP_Server*)arg; HTTP_Server *S = (HTTP_Server*)arg;
while(1) { while(1) {
pthread_mutex_lock(&sessionMapLock); pthread_mutex_lock(&S->sessionMapLock);
// Wait here until the serviceConnections condition is signaled by the top_of_frame job. // Wait here until the serviceConnections condition is signaled by the top_of_frame job.
pthread_cond_wait(&serviceConnections, &sessionMapLock); pthread_cond_wait(&S->serviceConnections, &S->sessionMapLock);
if (S->shutting_down) { if (S->shutting_down) {
pthread_mutex_unlock(&sessionMapLock); pthread_mutex_unlock(&S->sessionMapLock);
return NULL; return NULL;
} else { } else {
mg_mgr_poll(&S->mgr, 50); mg_mgr_poll(&S->mgr, 50);
} }
pthread_mutex_unlock(&sessionMapLock); pthread_mutex_unlock(&S->sessionMapLock);
} }
return NULL; return NULL;
} }
void HTTP_Server::install_API_GET_handler(std::string APIname, httpMethodHandler handler) {
pthread_mutex_lock(&APIMapLock);
httpMethodHandlerMap.insert(std::pair<std::string, httpMethodHandler>(APIname, handler));
pthread_mutex_unlock(&APIMapLock);
}
void HTTP_Server::handle_API_GET_request(struct mg_connection *nc, http_message *hm, std::string handlerName) {
std::map<std::string, httpMethodHandler>::iterator iter;
iter = httpMethodHandlerMap.find(handlerName);
if (iter != httpMethodHandlerMap.end()) {
httpMethodHandler handler = iter->second;
handler(nc, hm);
} else {
mg_http_send_error(nc, 404, "No such API.");
}
}
void HTTP_Server::sendSessionValues(struct mg_connection *nc) {
// Find the session that goes with the given websocket connection,
// and tell it to send its values to the client (web browser).
std::map<mg_connection*, WSsession*>::iterator iter;
iter = sessionMap.find(nc);
if (iter != sessionMap.end()) {
WSsession* session = iter->second;
session->sendValues();
}
}
void HTTP_Server::deleteSession(struct mg_connection *nc) {
std::map<mg_connection*, WSsession*>::iterator iter;
iter = sessionMap.find(nc);
if (iter != sessionMap.end()) {
WSsession* session = iter->second;
delete session;
sessionMap.erase(iter);
}
}
void HTTP_Server::handleClientMessage(struct mg_connection *nc, std::string msg) {
std::map<mg_connection*, WSsession*>::iterator iter;
iter = sessionMap.find(nc);
if (iter != sessionMap.end()) {
WSsession* session = iter->second;
session->handle_msg(msg);
}
}
void HTTP_Server::addSession(struct mg_connection *nc, WSsession* session) {
pthread_mutex_lock(&sessionMapLock);
sessionMap.insert( std::pair<mg_connection*, WSsession*>(nc, session) );
pthread_mutex_unlock(&sessionMapLock);
}
// ========================================================================= // =========================================================================
// Trick Sim Interface Functions // Trick Sim Interface Functions
// ========================================================================= // =========================================================================
int http_default_data(HTTP_Server *S) { int HTTP_Server::http_default_data() {
S->port = "8888"; port = "8888";
S->shutting_down = false; document_root = "www";
S->document_root = "www"; shutting_down = false;
return 0; return 0;
} }
int http_init(HTTP_Server *S) { int HTTP_Server::http_init() {
http_server_options.document_root = S->document_root; http_server_options.document_root = document_root;
http_server_options.enable_directory_listing = "yes"; http_server_options.enable_directory_listing = "yes";
mg_mgr_init(&S->mgr, NULL); install_API_GET_handler("vs_connections", &handle_HTTP_GET_vs_connections);
install_API_GET_handler("alloc_info", &handle_HTTP_GET_alloc_info);
std::cout << "Trick Webserver: Starting, and listening on port " << S->port << ".\n" mg_mgr_init( &mgr, NULL );
<< "Trick Webserver: Document root = \"" << S->document_root << "\""
<< std::endl;
S->nc = mg_bind(&S->mgr, S->port, ev_handler); memset(&bind_opts, 0, sizeof(bind_opts));
if (S->nc == NULL) { bind_opts.user_data = this;
std::cerr << "Trick Webserver: ERROR: Failed to create listener.\n" listener = mg_bind_opt( &mgr, port, ev_handler, bind_opts);
<< "Perhaps another program is already using port " << S->port << "."
<< std::endl; if (listener != NULL) {
return 1; std::cout << "Trick Webserver: Starting, and listening on port " << port << ".\n"
<< "Trick Webserver: Document root = \"" << document_root << "\""
<< std::endl;
} else {
std::cerr << "Trick Webserver: ERROR: Failed to create listener.\n"
<< "Perhaps another program is already using port " << port << "."
<< std::endl;
return 1;
} }
mg_set_protocol_http_websocket(S->nc); mg_set_protocol_http_websocket( listener );
pthread_cond_init(&serviceConnections, NULL); pthread_cond_init(&serviceConnections, NULL);
pthread_create( &S->server_thread, NULL, connectionAttendant, (void*)S); pthread_create( &server_thread, NULL, connectionAttendant, (void*)this );
return 0; return 0;
} }
int http_top_of_frame(HTTP_Server * S) { int HTTP_Server::http_top_of_frame() {
if (S->nc != NULL) { if (listener != NULL) {
// Have all of the sessions stage their data. We do this here, in a // Have all of the sessions stage their data. We do this here, in a
// top_of_frame job, so that all of the data is time-homogeneous. // top_of_frame job, so that all of the data is time-homogeneous.
std::map<mg_connection*, WSsession*>::iterator iter; std::map<mg_connection*, WSsession*>::iterator iter;
@ -268,21 +225,20 @@ int http_top_of_frame(HTTP_Server * S) {
session->stageValuesSynchronously(); session->stageValuesSynchronously();
} }
pthread_mutex_unlock(&sessionMapLock); pthread_mutex_unlock(&sessionMapLock);
// Signal the server thread to construct and send the values-message to the client. // Signal the server thread to construct and send the values-message to the client.
pthread_cond_signal( &serviceConnections ); pthread_cond_signal( &serviceConnections );
} }
return 0; return 0;
} }
int http_shutdown(HTTP_Server *S) { int HTTP_Server::http_shutdown() {
if (S->nc != NULL) { if (listener != NULL) {
std::cout << "Trick Webserver: Shutting down on port " << S->port << "." << std::endl; std::cout << "Trick Webserver: Shutting down on port " << port << "." << std::endl;
S->shutting_down = true; shutting_down = true;
// Send the serviceConnections signal one last time so the connectionAttendant thread can quit. // Send the serviceConnections signal one last time so the connectionAttendant thread can quit.
pthread_cond_signal( &serviceConnections ); pthread_cond_signal( &serviceConnections );
pthread_join(S->server_thread, NULL); pthread_join(server_thread, NULL);
} }
return 0; return 0;
} }