Changeset - c538d9965b82
[Not reviewed]
0 4 1
HanzZ - 13 years ago 2012-09-12 13:04:39
hanzz.k@gmail.com
Handle backendConfig
5 files changed with 88 insertions and 4 deletions:
0 comments (0 inline, 0 general)
include/transport/config.h
Show inline comments
 
@@ -38,84 +38,91 @@
 
#define CONFIG_STRING_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as<std::string>() : DEF)
 
#define CONFIG_BOOL_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as<bool>() : DEF)
 

	
 

	
 
namespace Transport {
 

	
 
/// Represents variable:value pairs.
 
typedef boost::program_options::variables_map Variables;
 

	
 
/// Represents config file.
 

	
 
/// It's used to load config file and allows others parts of libtransport to be configured
 
/// properly. Config files are text files which use "ini" format. Variables are divided into multiple
 
/// sections. Every class is configurable with some variables which change its behavior. Check particular
 
/// class documentation to get a list of all relevant variables for that class.
 
class Config {
 
	public:
 
		typedef std::map<std::string, boost::program_options::variable_value> SectionValuesCont;
 
		typedef std::map<std::string, boost::program_options::variable_value> UnregisteredCont;
 

	
 
		/// Constructor.
 
		Config(int argc = 0, char **argv = NULL) : m_argc(argc), m_argv(argv) {}
 

	
 
		/// Destructor
 
		virtual ~Config() {}
 

	
 
		/// Loads data from config file.
 
		
 
		/// You can pass your extra options which will be recognized by
 
		/// the parser using opts parameter.
 
		/// \param configfile path to config file
 
		/// \param opts extra options which will be recognized by a parser
 
		bool load(const std::string &configfile, boost::program_options::options_description &opts, const std::string &jid = "");
 

	
 
		bool load(std::istream &ifs, boost::program_options::options_description &opts, const std::string &jid = "");
 

	
 
		bool load(std::istream &ifs);
 

	
 
		/// Loads data from config file.
 
		
 
		/// This function loads only config variables needed by libtransport.
 
		/// \see load(const std::string &, boost::program_options::options_description &)
 
		/// \param configfile path to config file
 
		bool load(const std::string &configfile, const std::string &jid = "");
 

	
 
		bool reload();
 

	
 
		bool hasKey(const std::string &key) {
 
			return m_variables.find(key) != m_variables.end() || m_unregistered.find(key) != m_unregistered.end();
 
			return (m_variables.find(key) != m_variables.end() || m_unregistered.find(key) != m_unregistered.end()
 
					|| m_backendConfig.find(key) != m_backendConfig.end());
 
		}
 

	
 
		/// Returns value of variable defined by key.
 
		
 
		/// For variables in sections you can use "section.variable" key format.
 
		/// \param key config variable name
 
		const boost::program_options::variable_value &operator[] (const std::string &key) {
 
			if (m_variables.find(key) != m_variables.end()) {
 
				return m_variables[key];
 
			}
 
			if (m_backendConfig.find(key) != m_backendConfig.end()) {
 
				return m_backendConfig[key];
 
			}
 
			return m_unregistered[key];
 
		}
 

	
 
		SectionValuesCont getSectionValues(const std::string& sectionName);
 
 
 
		std::string getCommandLineArgs() const;
 

	
 
		/// Returns path to config file from which data were loaded.
 
		const std::string &getConfigFile() { return m_file; }
 

	
 
		/// This signal is emitted when config is loaded/reloaded.
 
		boost::signal<void ()> onConfigReloaded;
 

	
 
		void updateBackendConfig(const std::string &backendConfig);
 

	
 
		static Config *createFromArgs(int argc, char **argv, std::string &error, std::string &host, int &port);
 
	
 
	private:
 
		int m_argc;
 
		char **m_argv;
 
		Variables m_variables;
 
		Variables m_backendConfig;
 
		std::map<std::string, boost::program_options::variable_value> m_unregistered;
 
		std::string m_file;
 
		std::string m_jid;
 
};
 

	
 
}
include/transport/networkpluginserver.h
Show inline comments
 
@@ -63,96 +63,97 @@ class NetworkPluginServer {
 
		NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager);
 

	
 
		virtual ~NetworkPluginServer();
 

	
 
		void setAdminInterface(AdminInterface *adminInterface) {
 
			m_adminInterface = adminInterface;
 
		}
 

	
 
		int getBackendCount() {
 
			return m_clients.size();
 
		}
 

	
 
		const std::list<Backend *> &getBackends() {
 
			return m_clients;
 
		}
 

	
 
		const std::vector<std::string> &getCrashedBackends() {
 
			return m_crashedBackends;
 
		}
 

	
 
		void collectBackend();
 

	
 
		bool moveToLongRunBackend(User *user);
 

	
 
		void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &message);
 

	
 
	private:
 
		void handleNewClientConnection(boost::shared_ptr<Swift::Connection> c);
 
		void handleSessionFinished(Backend *c);
 
		void handlePongReceived(Backend *c);
 
		void handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data);
 

	
 
		void handleConnectedPayload(const std::string &payload);
 
		void handleDisconnectedPayload(const std::string &payload);
 
		void handleBuddyChangedPayload(const std::string &payload);
 
		void handleBuddyRemovedPayload(const std::string &payload);
 
		void handleConvMessagePayload(const std::string &payload, bool subject = false);
 
		void handleParticipantChangedPayload(const std::string &payload);
 
		void handleRoomChangedPayload(const std::string &payload);
 
		void handleVCardPayload(const std::string &payload);
 
		void handleChatStatePayload(const std::string &payload, Swift::ChatState::ChatStateType type);
 
		void handleAuthorizationPayload(const std::string &payload);
 
		void handleAttentionPayload(const std::string &payload);
 
		void handleStatsPayload(Backend *c, const std::string &payload);
 
		void handleFTStartPayload(const std::string &payload);
 
		void handleFTFinishPayload(const std::string &payload);
 
		void handleFTDataPayload(Backend *b, const std::string &payload);
 
		void handleQueryPayload(Backend *b, const std::string &payload);
 
		void handleBackendConfigPayload(const std::string &payload);
 

	
 
		void handleUserCreated(User *user);
 
		void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password);
 
		void handleRoomLeft(User *user, const std::string &room);
 
		void handleUserReadyToConnect(User *user);
 
		void handleUserPresenceChanged(User *user, Swift::Presence::ref presence);
 
		void handleUserDestroyed(User *user);
 

	
 
		void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleBuddyRemoved(Buddy *buddy);
 
		void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item);
 

	
 
		void handleBlockToggled(Buddy *buddy);
 

	
 
		void handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> vcard);
 
		void handleVCardRequired(User *user, const std::string &name, unsigned int id);
 

	
 
		void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id);
 
		void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID);
 
		void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size);
 
		void handleFTDataNeeded(Backend *b, unsigned long ftid);
 

	
 
		void send(boost::shared_ptr<Swift::Connection> &, const std::string &data);
 

	
 
		void pingTimeout();
 
		void sendPing(Backend *c);
 
		Backend *getFreeClient(bool acceptUsers = true, bool longRun = false);
 

	
 
		UserManager *m_userManager;
 
		VCardResponder *m_vcardResponder;
 
		RosterResponder *m_rosterResponder;
 
		BlockResponder *m_blockResponder;
 
		Config *m_config;
 
		boost::shared_ptr<Swift::ConnectionServer> m_server;
 
		std::list<Backend *>  m_clients;
 
		Swift::Timer::ref m_pingTimer;
 
		Swift::Timer::ref m_collectTimer;
 
		Component *m_component;
 
		std::list<User *> m_waitingUsers;
 
		bool m_isNextLongRun;
 
		std::map<unsigned long, FileTransferManager::Transfer> m_filetransfers;
 
		FileTransferManager *m_ftManager;
 
		std::vector<std::string> m_crashedBackends;
 
		AdminInterface *m_adminInterface;
 
		bool m_startingBackend;
 
};
 

	
 
}
src/config.cpp
Show inline comments
 
@@ -239,96 +239,110 @@ bool Config::load(const std::string &configfile, const std::string &jid) {
 
		options_description opts("Transport options");
 
		return load(configfile, opts, jid);
 
	} catch ( const boost::program_options::multiple_occurrences& e ) {
 
#if (BOOST_MAJOR_VERSION >= 1 && BOOST_MINOR_VERSION >= 42)
 
		std::cerr << configfile << " parsing error: " << e.what() << " from option: " << e.get_option_name() << std::endl;
 
#else
 
		std::cerr << configfile << " parsing error: " << e.what() << std::endl;
 
#endif
 
		return false;
 
	}
 
}
 

	
 
bool Config::reload() {
 
	if (m_file.empty()) {
 
		return false;
 
	}
 

	
 
	return load(m_file, m_jid);
 
}
 

	
 
Config::SectionValuesCont Config::getSectionValues(const std::string& sectionName) {
 
	SectionValuesCont sectionValues;
 

	
 
	std::string sectionSearchString = sectionName + ".";
 
	BOOST_FOREACH (const Variables::value_type & varItem, m_variables) {
 
		if (boost::istarts_with(varItem.first, sectionSearchString))
 
			sectionValues[varItem.first] = varItem.second;
 
	}
 

	
 
	BOOST_FOREACH (const UnregisteredCont::value_type & varItem, m_unregistered) {
 
		if (boost::istarts_with(varItem.first, sectionSearchString))
 
			sectionValues[varItem.first] = varItem.second;
 
	}
 

	
 
	return sectionValues;
 
}
 

	
 
std::string Config::getCommandLineArgs() const {
 
	std::ostringstream commandLineArgs;
 

	
 
	// Return the command-line arguments that were passed to us originally (but remove the initial .exe part)
 
	for (int i = 1; i < m_argc; ++i) 	{
 
		commandLineArgs << "\"" << m_argv[i] << "\" ";
 
	}
 

	
 
	return commandLineArgs.str();
 
}
 

	
 
void Config::updateBackendConfig(const std::string &backendConfig) {
 
	options_description opts("Backend options");
 
	opts.add_options()
 
		("registration.needPassword", value<bool>()->default_value(true), "")
 
		("registration.extraField", value<std::vector<std::string> >()->multitoken(), "")
 
	;
 

	
 
	std::stringstream ifs(backendConfig);
 
	parsed_options parsed = parse_config_file(ifs, opts, true);
 

	
 
	store(parsed, m_backendConfig);
 
	notify(m_backendConfig);
 
}
 

	
 
Config *Config::createFromArgs(int argc, char **argv, std::string &error, std::string &host, int &port) {
 
	std::string jid;
 
	std::ostringstream os;
 
	std::string configFile;
 
	boost::program_options::variables_map vm;
 
	boost::program_options::options_description desc("Usage: spectrum <config_file.cfg>\nAllowed options");
 
	desc.add_options()
 
		("help", "help")
 
		("host,h", boost::program_options::value<std::string>(&host)->default_value(""), "Host to connect to")
 
		("port,p", boost::program_options::value<int>(&port)->default_value(10000), "Port to connect to")
 
		("no-daemonize,n", "Do not run spectrum as daemon")
 
		("no-debug,d", "Create coredumps on crash")
 
		("jid,j", boost::program_options::value<std::string>(&jid)->default_value(""), "Specify JID of transport manually")
 
		("config", boost::program_options::value<std::string>(&configFile)->default_value(""), "Config file")
 
		;
 

	
 
	os << desc;
 
	try
 
	{
 
		boost::program_options::positional_options_description p;
 
		p.add("config", -1);
 
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
 
			options(desc).positional(p).allow_unregistered().run(), vm);
 
		boost::program_options::notify(vm);
 
			
 
		if(vm.count("help"))
 
		{
 
			error = os.str();
 
			return NULL;
 
		}
 

	
 
		if(vm.count("config") == 0) {
 
			error = os.str();
 
			return NULL;
 
		}
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		error = os.str();
 
		return NULL;
 
	}
 
	catch (...)
 
	{
 
		error = os.str();
 
		return NULL;
 
	}
 

	
 
	Config *config = new Config(argc, argv);
src/networkpluginserver.cpp
Show inline comments
 
@@ -780,201 +780,214 @@ void NetworkPluginServer::handlePongReceived(Backend *c) {
 
		// Backend is fully ready to handle requests
 
		c->willDie = false;
 

	
 
		if (m_clients.size() == 1) {
 
			// first backend connected, start the server, we're ready.
 
			m_component->start();
 
		}
 

	
 
		// some users are in queue waiting for this backend
 
		while(!m_waitingUsers.empty()) {
 
			// There's no new backend, so stop associating users and wait for new backend,
 
			// which has been already spawned in getFreeClient() call.
 
			if (getFreeClient() == NULL)
 
				break;
 

	
 
			User *u = m_waitingUsers.front();
 
			m_waitingUsers.pop_front();
 

	
 
			LOG4CXX_INFO(logger, "Associating " << u->getJID().toString() << " with this backend");
 

	
 
			// associate backend with user
 
			handleUserCreated(u);
 

	
 
			// connect user if it's ready
 
			if (u->isReadyToConnect()) {
 
				handleUserReadyToConnect(u);
 
			}
 
		}
 
	}
 

	
 
	c->pongReceived = true;
 
}
 

	
 
void NetworkPluginServer::handleQueryPayload(Backend *b, const std::string &data) {
 
	pbnetwork::BackendConfig payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	if (!m_adminInterface) {
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(payload.config());
 
	m_adminInterface->handleQuery(msg);
 

	
 
	pbnetwork::BackendConfig vcard;
 
	vcard.set_config(msg->getBody());
 
	pbnetwork::BackendConfig response;
 
	response.set_config(msg->getBody());
 

	
 
	std::string message;
 
	vcard.SerializeToString(&message);
 
	response.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY);
 

	
 
	send(b->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleBackendConfigPayload(const std::string &data) {
 
	pbnetwork::BackendConfig payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	m_config->updateBackendConfig(payload.config());
 
}
 

	
 
void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data) {
 
	// Append data to buffer
 
	c->data.insert(c->data.end(), data->begin(), data->end());
 

	
 
	// Parse data while there are some
 
	while (c->data.size() != 0) {
 
		// expected_size of wrapper message
 
		unsigned int expected_size;
 

	
 
		// if data is >= 4, we have whole header and we can
 
		// read expected_size.
 
		if (c->data.size() >= 4) {
 
			expected_size = *((unsigned int*) &c->data[0]);
 
			expected_size = ntohl(expected_size);
 
			// If we don't have whole wrapper message, wait for next
 
			// handleDataRead call.
 
			if (c->data.size() - 4 < expected_size)
 
				return;
 
		}
 
		else {
 
			return;
 
		}
 

	
 
		// Parse wrapper message and erase it from buffer.
 
		pbnetwork::WrapperMessage wrapper;
 
		if (wrapper.ParseFromArray(&c->data[4], expected_size) == false) {
 
			std::cout << "PARSING ERROR " << expected_size << "\n";
 
			c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size);
 
			continue;
 
		}
 
		c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size);
 

	
 
		// Handle payload in wrapper message
 
		switch(wrapper.type()) {
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONNECTED:
 
				handleConnectedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_DISCONNECTED:
 
				handleDisconnectedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED:
 
				handleBuddyChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE:
 
				handleConvMessagePayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED:
 
				handleConvMessagePayload(wrapper.payload(), true);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_PONG:
 
				handlePongReceived(c);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_PARTICIPANT_CHANGED:
 
				handleParticipantChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_NICKNAME_CHANGED:
 
				handleRoomChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_VCARD:
 
				handleVCardPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Composing);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Paused);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Active);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_AUTH_REQUEST:
 
				handleAuthorizationPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION:
 
				handleAttentionPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_STATS:
 
				handleStatsPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_START:
 
				handleFTStartPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH:
 
				handleFTFinishPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_DATA:
 
				handleFTDataPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED:
 
				handleBuddyRemovedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_QUERY:
 
				handleQueryPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG:
 
				handleBackendConfigPayload(wrapper.payload());
 
				break;
 
			default:
 
				return;
 
		}
 
	}
 
}
 

	
 
void NetworkPluginServer::send(boost::shared_ptr<Swift::Connection> &c, const std::string &data) {
 
	// generate header - size of wrapper message
 
	uint32_t size = htonl(data.size());
 
	char *header = (char *) &size;
 

	
 
	// send header together with wrapper message
 
	c->write(Swift::createSafeByteArray(std::string(header, 4) + data));
 
}
 

	
 
void NetworkPluginServer::pingTimeout() {
 
	// TODO: move to separate timer, those 2 loops could be expensive
 
	// Some users are connected for weeks and they are blocking backend to be destroyed and its memory
 
	// to be freed. We are finding users who are inactive for more than "idle_reconnect_time" seconds and
 
	// reconnect them to long-running backend, where they can idle hapilly till the end of ages.
 
	time_t now = time(NULL);
 
	std::vector<User *> usersToMove;
 
	unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time");
 
	if (diff != 0) {
 
		for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
			// Users from long-running backends can't be moved
 
			if ((*it)->longRun) {
 
				continue;
 
			}
 

	
 
			// Find users which are inactive for more than 'diff'
 
			BOOST_FOREACH(User *u, (*it)->users) {
 
				if (now - u->getLastActivity() > diff) {
 
					usersToMove.push_back(u);
 
				}
 
			}
 
		}
 

	
 
		// Move inactive users to long-running backend.
 
		BOOST_FOREACH(User *u, usersToMove) {
 
			LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend");
 
			if (!moveToLongRunBackend(u))
 
				break;
 
		}
 
	}
 

	
 
	// We have to remove startingBackend flag otherwise 1 broken backend start could
 
	// block the backend.
src/tests/config.cpp
Show inline comments
 
new file 100644
 
#include "transport/userregistry.h"
 
#include "transport/config.h"
 
#include "transport/storagebackend.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/conversation.h"
 
#include "transport/usermanager.h"
 
#include "transport/localbuddy.h"
 
#include <cppunit/TestFixture.h>
 
#include <cppunit/extensions/HelperMacros.h>
 
#include <Swiften/Swiften.h>
 
#include <Swiften/EventLoop/DummyEventLoop.h>
 
#include <Swiften/Server/Server.h>
 
#include <Swiften/Network/DummyNetworkFactories.h>
 
#include <Swiften/Network/DummyConnectionServer.h>
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Server/ServerFromClientSession.h"
 
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 
#include "basictest.h"
 

	
 
#include "transport/util.h"
 

	
 
using namespace Transport;
 

	
 
class ConfigTest : public CPPUNIT_NS :: TestFixture{
 
	CPPUNIT_TEST_SUITE(ConfigTest);
 
	CPPUNIT_TEST(updateBackendConfig);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		void setUp (void) {
 
		}
 

	
 
		void tearDown (void) {
 

	
 
		}
 

	
 
	void updateBackendConfig() {
 
		Config cfg;
 
		CPPUNIT_ASSERT(!cfg.hasKey("registration.needPassword"));
 

	
 
		cfg.updateBackendConfig("[registration]\nneedPassword=0\n");
 
		CPPUNIT_ASSERT(cfg.hasKey("registration.needPassword"));
 
		CPPUNIT_ASSERT_EQUAL(false, CONFIG_BOOL(&cfg, "registration.needPassword"));
 
	}
 

	
 
};
 

	
 
CPPUNIT_TEST_SUITE_REGISTRATION (ConfigTest);
0 comments (0 inline, 0 general)