Added subscription service capability.
This commit is contained in:
parent
0a03b3553d
commit
ec39c1df1a
12
Command.cpp
12
Command.cpp
@ -12,16 +12,4 @@ namespace core {
|
|||||||
out << "Write your own command description here for the help system." << std::endl;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
32
Command.h
32
Command.h
@ -23,22 +23,6 @@ namespace core {
|
|||||||
|
|
||||||
public:
|
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 method is used to implement the functionality of the requested command.
|
||||||
/// This pure virtual function must be implemented in your inheriting object.
|
/// This pure virtual function must be implemented in your inheriting object.
|
||||||
@ -60,22 +44,6 @@ namespace core {
|
|||||||
|
|
||||||
virtual void output(std::stringstream &out);
|
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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,20 @@ namespace core {
|
|||||||
CommandList::CommandList(std::string delimiter) : delimiter(delimiter) {}
|
CommandList::CommandList(std::string delimiter) : delimiter(delimiter) {}
|
||||||
|
|
||||||
void CommandList::add(Command &command, std::string name) {
|
void CommandList::add(Command &command, std::string name) {
|
||||||
command.setName(name);
|
commands.insert(std::make_pair(name, &command));
|
||||||
commands.push_back(&command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandList::remove(Command &command) {}
|
void CommandList::remove(Command &command) {}
|
||||||
|
|
||||||
bool CommandList::processRequest(coreutils::ZString &request, TCPSession &session) {
|
bool CommandList::processRequest(coreutils::ZString &request, TCPSession &session) {
|
||||||
if(session.grab != NULL)
|
if(session.grab != NULL)
|
||||||
return session.grab->processCommand(request, session);
|
return session.grab->processCommand(request, session);
|
||||||
else {
|
else {
|
||||||
|
if(request.equals(""))
|
||||||
|
return false;
|
||||||
request.split(delimiter);
|
request.split(delimiter);
|
||||||
for(auto *command : commands)
|
auto command = commands.find(request[0].str())->second;
|
||||||
if(command->check(request))
|
return command->processCommand(request, session);
|
||||||
return command->processCommand(request, session);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -34,8 +34,8 @@ namespace core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) {
|
int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) {
|
||||||
for(Command *command : commands)
|
// for(Command *command : commands)
|
||||||
session.out << command->getName() << std::endl;
|
// session.out << command->getName() << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ namespace core {
|
|||||||
/// The vector of all registered commands.
|
/// The vector of all registered commands.
|
||||||
///
|
///
|
||||||
|
|
||||||
std::vector<Command *> commands;
|
std::map<std::string, Command *> commands;
|
||||||
std::string delimiter;
|
std::string delimiter;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -167,7 +167,7 @@ namespace core {
|
|||||||
coreutils::Log(coreutils::LOG_DEBUG_2) << "Shutdown requested on socket " << descriptor << " with reason " << text << ".";
|
coreutils::Log(coreutils::LOG_DEBUG_2) << "Shutdown requested on socket " << descriptor << " with reason " << text << ".";
|
||||||
shutDown = true;
|
shutDown = true;
|
||||||
reset = false;
|
reset = false;
|
||||||
if(!needsToWrite())
|
// if(!needsToWrite())
|
||||||
delete this;
|
delete this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
2
Socket.h
2
Socket.h
@ -48,7 +48,7 @@ namespace core {
|
|||||||
/// Destructor
|
/// Destructor
|
||||||
///
|
///
|
||||||
|
|
||||||
~Socket();
|
virtual ~Socket();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Use the shutdown() method to terminate the socket connection and remove resources.
|
/// Use the shutdown() method to terminate the socket connection and remove resources.
|
||||||
|
38
Subscription.cpp
Normal file
38
Subscription.cpp
Normal 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
32
Subscription.h
Normal 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
56
SubscriptionManager.cpp
Normal 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
27
SubscriptionManager.h
Normal 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
|
@ -9,6 +9,13 @@ namespace core {
|
|||||||
TCPServer::TCPServer(EPoll &ePoll, IPAddress address, std::string delimiter, std::string text)
|
TCPServer::TCPServer(EPoll &ePoll, IPAddress address, std::string delimiter, std::string text)
|
||||||
: TCPSocket(ePoll, text), commands(delimiter) {
|
: 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));
|
setDescriptor(socket(AF_INET, SOCK_STREAM, 0));
|
||||||
int yes = 1;
|
int yes = 1;
|
||||||
setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
||||||
@ -27,7 +34,7 @@ namespace core {
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
TCPSession *session = accept();
|
TCPSession *session = accept();
|
||||||
if(session)
|
if(session)
|
||||||
sessions.push_back(session);
|
sessions.push_back(session);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "IPAddressList.h"
|
#include "IPAddressList.h"
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
#include "CommandList.h"
|
#include "CommandList.h"
|
||||||
|
#include "SubscriptionManager.h"
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ namespace core {
|
|||||||
/// The destructor for this object.
|
/// The destructor for this object.
|
||||||
///
|
///
|
||||||
|
|
||||||
~TCPServer();
|
virtual ~TCPServer();
|
||||||
|
|
||||||
virtual void sessionErrorHandler(std::string errorString, std::stringstream &out);
|
virtual void sessionErrorHandler(std::string errorString, std::stringstream &out);
|
||||||
|
|
||||||
@ -135,6 +136,7 @@ namespace core {
|
|||||||
|
|
||||||
TCPSession * accept();
|
TCPSession * accept();
|
||||||
std::mutex lock;
|
std::mutex lock;
|
||||||
|
SubscriptionManager subscriptions;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ namespace core {
|
|||||||
|
|
||||||
TCPSession::~TCPSession() {
|
TCPSession::~TCPSession() {
|
||||||
server.removeFromSessionList(this);
|
server.removeFromSessionList(this);
|
||||||
|
coreutils::Log(coreutils::LOG_DEBUG_1) << "Terminating TCPSession level.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPSession::output(std::stringstream &data) {
|
void TCPSession::output(std::stringstream &data) {
|
||||||
@ -17,7 +18,7 @@ namespace core {
|
|||||||
|
|
||||||
void TCPSession::protocol(coreutils::ZString &data) {
|
void TCPSession::protocol(coreutils::ZString &data) {
|
||||||
if(!server.commands.processRequest(data, *this)) {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ namespace core {
|
|||||||
protocol(blank);
|
protocol(blank);
|
||||||
send();
|
send();
|
||||||
if(term)
|
if(term)
|
||||||
shutdown("termination requested");
|
shutdown("termination requested");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPSession::onConnected() {}
|
void TCPSession::onConnected() {}
|
||||||
|
@ -35,7 +35,7 @@ namespace core {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
|
||||||
~TCPSession();
|
virtual ~TCPSession();
|
||||||
|
|
||||||
Command *grab = NULL;
|
Command *grab = NULL;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ namespace core {
|
|||||||
|
|
||||||
TCPSocket(EPoll &ePoll);
|
TCPSocket(EPoll &ePoll);
|
||||||
TCPSocket(EPoll &ePoll, std::string text);
|
TCPSocket(EPoll &ePoll, std::string text);
|
||||||
~TCPSocket();
|
virtual ~TCPSocket();
|
||||||
|
|
||||||
void connect(IPAddress &address);
|
void connect(IPAddress &address);
|
||||||
|
|
||||||
|
@ -4,5 +4,9 @@
|
|||||||
"path": "."
|
"path": "."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {}
|
"settings": {
|
||||||
|
"files.associations": {
|
||||||
|
"iosfwd": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user