Added subscription service capability.

This commit is contained in:
Brad Arant 2021-08-31 17:57:21 -07:00
parent 0a03b3553d
commit ec39c1df1a
16 changed files with 220 additions and 97 deletions

View File

@ -12,16 +12,4 @@ namespace core {
out << "Write your own command description here for the help system." << std::endl;
}
bool Command::check(coreutils::ZString &request) {
return request[0].equals(name);
}
void Command::setName(std::string name) {
this->name = name;
}
std::string Command::getName() {
return name;
}
}

View File

@ -23,22 +23,6 @@ namespace core {
public:
///
/// Implement check method to provide a special check rule upon the request to see
/// if the command should be processed.
///
/// The default rule is to verify that the first token in the request string matches
/// the name given on the registration of the command to the CommandList. This can
/// be overridden by implementing the check() method to perform the test and return
/// the condition of the command.
///
/// @param request The request passed to the parser to check the rule.
/// @return Return true to execute the command. Returning false will cause no action
/// on this command.
///
virtual bool check(coreutils::ZString &request);
///
/// This method is used to implement the functionality of the requested command.
/// This pure virtual function must be implemented in your inheriting object.
@ -60,22 +44,6 @@ namespace core {
virtual void output(std::stringstream &out);
///
/// Set the name of this command used in default rule checking during request parsing.
/// NOTE: You do not need to call this under normal conditions as adding a Command
/// to a CommandList using the add() method contains a parameter to pass the name
/// of the Command.
///
/// @param name Specify the name of this command for default parsing.
///
void setName(std::string name);
std::string getName();
private:
std::string name;
};
}

View File

@ -6,8 +6,7 @@ namespace core {
CommandList::CommandList(std::string delimiter) : delimiter(delimiter) {}
void CommandList::add(Command &command, std::string name) {
command.setName(name);
commands.push_back(&command);
commands.insert(std::make_pair(name, &command));
}
void CommandList::remove(Command &command) {}
@ -16,9 +15,10 @@ namespace core {
if(session.grab != NULL)
return session.grab->processCommand(request, session);
else {
if(request.equals(""))
return false;
request.split(delimiter);
for(auto *command : commands)
if(command->check(request))
auto command = commands.find(request[0].str())->second;
return command->processCommand(request, session);
}
return false;
@ -34,8 +34,8 @@ namespace core {
}
int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) {
for(Command *command : commands)
session.out << command->getName() << std::endl;
// for(Command *command : commands)
// session.out << command->getName() << std::endl;
return true;
}

View File

@ -68,7 +68,7 @@ namespace core {
/// The vector of all registered commands.
///
std::vector<Command *> commands;
std::map<std::string, Command *> commands;
std::string delimiter;
};

View File

@ -167,7 +167,7 @@ namespace core {
coreutils::Log(coreutils::LOG_DEBUG_2) << "Shutdown requested on socket " << descriptor << " with reason " << text << ".";
shutDown = true;
reset = false;
if(!needsToWrite())
// if(!needsToWrite())
delete this;
}

View File

@ -48,7 +48,7 @@ namespace core {
/// Destructor
///
~Socket();
virtual ~Socket();
///
/// Use the shutdown() method to terminate the socket connection and remove resources.

38
Subscription.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "Subscription.h"
namespace core {
Subscription::Subscription(std::string id, TCPSession &session) : id(id), owner(&session) {}
int Subscription::subscribe(TCPSession &session) {
subscribers.push_back(&session);
return 1;
}
int Subscription::unsubscribe(TCPSession &session) {
for(auto subscriber = subscribers.begin(); subscriber < subscribers.end(); ++subscriber) {
if(*subscriber == &session) {
subscribers.erase(subscriber);
return 1;
}
}
return 0;
}
int Subscription::process(coreutils::ZString &request, std::stringstream &out) {
out << "event:" << request[1] << ":" << request[2] << std::endl;
return 1;
}
int Subscription::event(std::stringstream &out) {
for(auto subscriber = subscribers.begin(); subscriber < subscribers.end(); ++subscriber)
(*subscriber)->write(out.str());
return 1;
}
int Subscription::processCommand(coreutils::ZString &request, TCPSession &session) {
session.out << "Generic subscription passthrough";
return 1;
}
}

32
Subscription.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __Subscription_h__
#define __Subscription_h__
#include "TCPSession.h"
#include "Command.h"
#include "ZString.h"
#include <vector>
namespace core {
class Subscription : public Command {
public:
Subscription(std::string id, TCPSession &session);
int subscribe(TCPSession &session);
int unsubscribe(TCPSession &session);
virtual int process(coreutils::ZString &request, std::stringstream &out);
int event(std::stringstream &out);
int processCommand(coreutils::ZString &request, TCPSession &session) override;
std::string id;
TCPSession *owner;
std::vector<TCPSession *> subscribers;
};
}
#endif

56
SubscriptionManager.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "SubscriptionManager.h"
#include "Log.h"
namespace core {
SubscriptionManager::SubscriptionManager() {}
int SubscriptionManager::removeSessionSubscriptions(TCPSession &session) {
int count = 0;
for(auto subscription = subscriptions.begin(); subscription < subscriptions.end(); ++subscription) {
count += (*subscription)->unsubscribe(session);
}
coreutils::Log(coreutils::LOG_DEBUG_2) << "Removed session from " << count << " subscriptions.";
return count;
}
int SubscriptionManager::processCommand(coreutils::ZString &request, TCPSession &session) {
if(request[0].equals("publish")) {
subscriptions.push_back(new Subscription(request[1].str(), session));
return 1;
} else if(request[0].equals("catalog")) {
session.out << ":catalog:";
for(auto subscription = subscriptions.begin(); subscription < subscriptions.end(); ++subscription) {
session.out << (*subscription)->id << "|";
(*subscription)->processCommand(request, session);
session.out << (";");
}
session.out << std::endl;
return 1;
}
for(auto subscription = subscriptions.begin(); subscription < subscriptions.end(); ++subscription) {
if(request[1].equals((*subscription)->id)) {
if(request[0].equals("unpublish")) {
subscriptions.erase(subscription);
} else if(request[0].equals("subscribe")) {
(*subscription)->subscribe(session);
} else if(request[0].equals("unsubscribe")) {
(*subscription)->unsubscribe(session);
} else if(request[0].equals("event")) {
if((*subscription)->owner == &session) {
std::stringstream out;
(*subscription)->process(request, out);
(*subscription)->event(out);
}
else
return 0;
}
return 1;
}
}
return 0;
}
}

27
SubscriptionManager.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __SubscriptionManager_h__
#define __SubscriptionManager_h__
#include "TCPSession.h"
#include "Subscription.h"
#include "Command.h"
#include "ZString.h"
#include <vector>
namespace core {
class SubscriptionManager : public Command {
public:
SubscriptionManager();
int removeSessionSubscriptions(TCPSession &session);
int processCommand(coreutils::ZString &request, TCPSession &session) override;
private:
std::vector<Subscription *> subscriptions;
};
}
#endif

View File

@ -9,6 +9,13 @@ namespace core {
TCPServer::TCPServer(EPoll &ePoll, IPAddress address, std::string delimiter, std::string text)
: TCPSocket(ePoll, text), commands(delimiter) {
commands.add(subscriptions, "publish");
commands.add(subscriptions, "unpublish");
commands.add(subscriptions, "subscribe");
commands.add(subscriptions, "unsubscribe");
commands.add(subscriptions, "catalog");
commands.add(subscriptions, "event");
setDescriptor(socket(AF_INET, SOCK_STREAM, 0));
int yes = 1;
setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

View File

@ -6,6 +6,7 @@
#include "IPAddressList.h"
#include "Command.h"
#include "CommandList.h"
#include "SubscriptionManager.h"
namespace core {
@ -40,7 +41,7 @@ namespace core {
/// The destructor for this object.
///
~TCPServer();
virtual ~TCPServer();
virtual void sessionErrorHandler(std::string errorString, std::stringstream &out);
@ -135,6 +136,7 @@ namespace core {
TCPSession * accept();
std::mutex lock;
SubscriptionManager subscriptions;
};

View File

@ -9,6 +9,7 @@ namespace core {
TCPSession::~TCPSession() {
server.removeFromSessionList(this);
coreutils::Log(coreutils::LOG_DEBUG_1) << "Terminating TCPSession level.";
}
void TCPSession::output(std::stringstream &data) {
@ -17,7 +18,7 @@ namespace core {
void TCPSession::protocol(coreutils::ZString &data) {
if(!server.commands.processRequest(data, *this)) {
coreutils::Log(coreutils::LOG_DEBUG_1) << data.str();
coreutils::Log(coreutils::LOG_DEBUG_1) << "Received data could not be parsed: " << data.str();
}
}

View File

@ -35,7 +35,7 @@ namespace core {
///
///
~TCPSession();
virtual ~TCPSession();
Command *grab = NULL;

View File

@ -23,7 +23,7 @@ namespace core {
TCPSocket(EPoll &ePoll);
TCPSocket(EPoll &ePoll, std::string text);
~TCPSocket();
virtual ~TCPSocket();
void connect(IPAddress &address);

View File

@ -4,5 +4,9 @@
"path": "."
}
],
"settings": {}
"settings": {
"files.associations": {
"iosfwd": "c"
}
}
}