| |
| #include "XmlRpcServer.h" |
| #include "XmlRpcServerConnection.h" |
| #include "XmlRpcServerMethod.h" |
| #include "XmlRpcSocket.h" |
| #include "XmlRpcUtil.h" |
| #include "XmlRpcException.h" |
| |
| |
| using namespace XmlRpc; |
| |
| |
| XmlRpcServer::XmlRpcServer() |
| { |
| _introspectionEnabled = false; |
| _listMethods = 0; |
| _methodHelp = 0; |
| } |
| |
| |
| XmlRpcServer::~XmlRpcServer() |
| { |
| this->shutdown(); |
| _methods.clear(); |
| delete _listMethods; |
| delete _methodHelp; |
| } |
| |
| |
| // Add a command to the RPC server |
| void |
| XmlRpcServer::addMethod(XmlRpcServerMethod* method) |
| { |
| _methods[method->name()] = method; |
| } |
| |
| // Remove a command from the RPC server |
| void |
| XmlRpcServer::removeMethod(XmlRpcServerMethod* method) |
| { |
| MethodMap::iterator i = _methods.find(method->name()); |
| if (i != _methods.end()) |
| _methods.erase(i); |
| } |
| |
| // Remove a command from the RPC server by name |
| void |
| XmlRpcServer::removeMethod(const std::string& methodName) |
| { |
| MethodMap::iterator i = _methods.find(methodName); |
| if (i != _methods.end()) |
| _methods.erase(i); |
| } |
| |
| |
| // Look up a method by name |
| XmlRpcServerMethod* |
| XmlRpcServer::findMethod(const std::string& name) const |
| { |
| MethodMap::const_iterator i = _methods.find(name); |
| if (i == _methods.end()) |
| return 0; |
| return i->second; |
| } |
| |
| |
| // Create a socket, bind to the specified port, and |
| // set it in listen mode to make it available for clients. |
| bool |
| XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/) |
| { |
| int fd = XmlRpcSocket::socket(); |
| if (fd < 0) |
| { |
| XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| return false; |
| } |
| |
| this->setfd(fd); |
| |
| // Don't block on reads/writes |
| if ( ! XmlRpcSocket::setNonBlocking(fd)) |
| { |
| this->close(); |
| XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| return false; |
| } |
| |
| // Allow this port to be re-bound immediately so server re-starts are not delayed |
| if ( ! XmlRpcSocket::setReuseAddr(fd)) |
| { |
| this->close(); |
| XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| return false; |
| } |
| |
| // Bind to the specified port on the default interface |
| if ( ! XmlRpcSocket::bind(fd, port)) |
| { |
| this->close(); |
| XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| return false; |
| } |
| |
| // Set in listening mode |
| if ( ! XmlRpcSocket::listen(fd, backlog)) |
| { |
| this->close(); |
| XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| return false; |
| } |
| |
| XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd); |
| |
| // Notify the dispatcher to listen on this source when we are in work() |
| _disp.addSource(this, XmlRpcDispatch::ReadableEvent); |
| |
| return true; |
| } |
| |
| |
| // Process client requests for the specified time |
| void |
| XmlRpcServer::work(double msTime) |
| { |
| XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection"); |
| _disp.work(msTime); |
| } |
| |
| |
| |
| // Handle input on the server socket by accepting the connection |
| // and reading the rpc request. |
| unsigned |
| XmlRpcServer::handleEvent(unsigned /* mask */) |
| { |
| acceptConnection(); |
| return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd |
| } |
| |
| |
| // Accept a client connection request and create a connection to |
| // handle method calls from the client. |
| void |
| XmlRpcServer::acceptConnection() |
| { |
| int s = XmlRpcSocket::accept(this->getfd()); |
| XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s); |
| if (s < 0) |
| { |
| //this->close(); |
| XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| } |
| else if ( ! XmlRpcSocket::setNonBlocking(s)) |
| { |
| XmlRpcSocket::close(s); |
| XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); |
| } |
| else // Notify the dispatcher to listen for input on this source when we are in work() |
| { |
| XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection"); |
| _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent); |
| } |
| } |
| |
| |
| // Create a new connection object for processing requests from a specific client. |
| XmlRpcServerConnection* |
| XmlRpcServer::createConnection(int s) |
| { |
| // Specify that the connection object be deleted when it is closed |
| return new XmlRpcServerConnection(s, this, true); |
| } |
| |
| |
| void |
| XmlRpcServer::removeConnection(XmlRpcServerConnection* sc) |
| { |
| _disp.removeSource(sc); |
| } |
| |
| |
| // Stop processing client requests |
| void |
| XmlRpcServer::exit() |
| { |
| _disp.exit(); |
| } |
| |
| |
| // Close the server socket file descriptor and stop monitoring connections |
| void |
| XmlRpcServer::shutdown() |
| { |
| // This closes and destroys all connections as well as closing this socket |
| _disp.clear(); |
| } |
| |
| |
| // Introspection support |
| static const std::string LIST_METHODS("system.listMethods"); |
| static const std::string METHOD_HELP("system.methodHelp"); |
| static const std::string MULTICALL("system.multicall"); |
| |
| |
| // List all methods available on a server |
| class ListMethods : public XmlRpcServerMethod |
| { |
| public: |
| ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {} |
| |
| void execute(XmlRpcValue& /* params */, XmlRpcValue& result) |
| { |
| _server->listMethods(result); |
| } |
| |
| std::string help() { return std::string("List all methods available on a server as an array of strings"); } |
| }; |
| |
| |
| // Retrieve the help string for a named method |
| class MethodHelp : public XmlRpcServerMethod |
| { |
| public: |
| MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {} |
| |
| void execute(XmlRpcValue& params, XmlRpcValue& result) |
| { |
| if (params[0].getType() != XmlRpcValue::TypeString) |
| throw XmlRpcException(METHOD_HELP + ": Invalid argument type"); |
| |
| XmlRpcServerMethod* m = _server->findMethod(params[0]); |
| if ( ! m) |
| throw XmlRpcException(METHOD_HELP + ": Unknown method name"); |
| |
| result = m->help(); |
| } |
| |
| std::string help() { return std::string("Retrieve the help string for a named method"); } |
| }; |
| |
| |
| // Specify whether introspection is enabled or not. Default is enabled. |
| void |
| XmlRpcServer::enableIntrospection(bool enabled) |
| { |
| if (_introspectionEnabled == enabled) |
| return; |
| |
| _introspectionEnabled = enabled; |
| |
| if (enabled) |
| { |
| if ( ! _listMethods) |
| { |
| _listMethods = new ListMethods(this); |
| _methodHelp = new MethodHelp(this); |
| } else { |
| addMethod(_listMethods); |
| addMethod(_methodHelp); |
| } |
| } |
| else |
| { |
| removeMethod(LIST_METHODS); |
| removeMethod(METHOD_HELP); |
| } |
| } |
| |
| |
| void |
| XmlRpcServer::listMethods(XmlRpcValue& result) |
| { |
| int i = 0; |
| result.setSize(_methods.size()+1); |
| for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it) |
| result[i++] = it->first; |
| |
| // Multicall support is built into XmlRpcServerConnection |
| result[i] = MULTICALL; |
| } |
| |
| |
| |