Home | History | Annotate | Download | only in src
      1 
      2 #include "XmlRpcServer.h"
      3 #include "XmlRpcServerConnection.h"
      4 #include "XmlRpcServerMethod.h"
      5 #include "XmlRpcSocket.h"
      6 #include "XmlRpcUtil.h"
      7 #include "XmlRpcException.h"
      8 
      9 
     10 using namespace XmlRpc;
     11 
     12 
     13 XmlRpcServer::XmlRpcServer()
     14 {
     15   _introspectionEnabled = false;
     16   _listMethods = 0;
     17   _methodHelp = 0;
     18 }
     19 
     20 
     21 XmlRpcServer::~XmlRpcServer()
     22 {
     23   this->shutdown();
     24   _methods.clear();
     25   delete _listMethods;
     26   delete _methodHelp;
     27 }
     28 
     29 
     30 // Add a command to the RPC server
     31 void
     32 XmlRpcServer::addMethod(XmlRpcServerMethod* method)
     33 {
     34   _methods[method->name()] = method;
     35 }
     36 
     37 // Remove a command from the RPC server
     38 void
     39 XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
     40 {
     41   MethodMap::iterator i = _methods.find(method->name());
     42   if (i != _methods.end())
     43     _methods.erase(i);
     44 }
     45 
     46 // Remove a command from the RPC server by name
     47 void
     48 XmlRpcServer::removeMethod(const std::string& methodName)
     49 {
     50   MethodMap::iterator i = _methods.find(methodName);
     51   if (i != _methods.end())
     52     _methods.erase(i);
     53 }
     54 
     55 
     56 // Look up a method by name
     57 XmlRpcServerMethod*
     58 XmlRpcServer::findMethod(const std::string& name) const
     59 {
     60   MethodMap::const_iterator i = _methods.find(name);
     61   if (i == _methods.end())
     62     return 0;
     63   return i->second;
     64 }
     65 
     66 
     67 // Create a socket, bind to the specified port, and
     68 // set it in listen mode to make it available for clients.
     69 bool
     70 XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
     71 {
     72   int fd = XmlRpcSocket::socket();
     73   if (fd < 0)
     74   {
     75     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
     76     return false;
     77   }
     78 
     79   this->setfd(fd);
     80 
     81   // Don't block on reads/writes
     82   if ( ! XmlRpcSocket::setNonBlocking(fd))
     83   {
     84     this->close();
     85     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
     86     return false;
     87   }
     88 
     89   // Allow this port to be re-bound immediately so server re-starts are not delayed
     90   if ( ! XmlRpcSocket::setReuseAddr(fd))
     91   {
     92     this->close();
     93     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
     94     return false;
     95   }
     96 
     97   // Bind to the specified port on the default interface
     98   if ( ! XmlRpcSocket::bind(fd, port))
     99   {
    100     this->close();
    101     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
    102     return false;
    103   }
    104 
    105   // Set in listening mode
    106   if ( ! XmlRpcSocket::listen(fd, backlog))
    107   {
    108     this->close();
    109     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
    110     return false;
    111   }
    112 
    113   XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd);
    114 
    115   // Notify the dispatcher to listen on this source when we are in work()
    116   _disp.addSource(this, XmlRpcDispatch::ReadableEvent);
    117 
    118   return true;
    119 }
    120 
    121 
    122 // Process client requests for the specified time
    123 void
    124 XmlRpcServer::work(double msTime)
    125 {
    126   XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
    127   _disp.work(msTime);
    128 }
    129 
    130 
    131 
    132 // Handle input on the server socket by accepting the connection
    133 // and reading the rpc request.
    134 unsigned
    135 XmlRpcServer::handleEvent(unsigned /* mask */)
    136 {
    137   acceptConnection();
    138   return XmlRpcDispatch::ReadableEvent;		// Continue to monitor this fd
    139 }
    140 
    141 
    142 // Accept a client connection request and create a connection to
    143 // handle method calls from the client.
    144 void
    145 XmlRpcServer::acceptConnection()
    146 {
    147   int s = XmlRpcSocket::accept(this->getfd());
    148   XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
    149   if (s < 0)
    150   {
    151     //this->close();
    152     XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
    153   }
    154   else if ( ! XmlRpcSocket::setNonBlocking(s))
    155   {
    156     XmlRpcSocket::close(s);
    157     XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
    158   }
    159   else  // Notify the dispatcher to listen for input on this source when we are in work()
    160   {
    161     XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
    162     _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent);
    163   }
    164 }
    165 
    166 
    167 // Create a new connection object for processing requests from a specific client.
    168 XmlRpcServerConnection*
    169 XmlRpcServer::createConnection(int s)
    170 {
    171   // Specify that the connection object be deleted when it is closed
    172   return new XmlRpcServerConnection(s, this, true);
    173 }
    174 
    175 
    176 void
    177 XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
    178 {
    179   _disp.removeSource(sc);
    180 }
    181 
    182 
    183 // Stop processing client requests
    184 void
    185 XmlRpcServer::exit()
    186 {
    187   _disp.exit();
    188 }
    189 
    190 
    191 // Close the server socket file descriptor and stop monitoring connections
    192 void
    193 XmlRpcServer::shutdown()
    194 {
    195   // This closes and destroys all connections as well as closing this socket
    196   _disp.clear();
    197 }
    198 
    199 
    200 // Introspection support
    201 static const std::string LIST_METHODS("system.listMethods");
    202 static const std::string METHOD_HELP("system.methodHelp");
    203 static const std::string MULTICALL("system.multicall");
    204 
    205 
    206 // List all methods available on a server
    207 class ListMethods : public XmlRpcServerMethod
    208 {
    209 public:
    210   ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
    211 
    212   void execute(XmlRpcValue& /* params */, XmlRpcValue& result)
    213   {
    214     _server->listMethods(result);
    215   }
    216 
    217   std::string help() { return std::string("List all methods available on a server as an array of strings"); }
    218 };
    219 
    220 
    221 // Retrieve the help string for a named method
    222 class MethodHelp : public XmlRpcServerMethod
    223 {
    224 public:
    225   MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
    226 
    227   void execute(XmlRpcValue& params, XmlRpcValue& result)
    228   {
    229     if (params[0].getType() != XmlRpcValue::TypeString)
    230       throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
    231 
    232     XmlRpcServerMethod* m = _server->findMethod(params[0]);
    233     if ( ! m)
    234       throw XmlRpcException(METHOD_HELP + ": Unknown method name");
    235 
    236     result = m->help();
    237   }
    238 
    239   std::string help() { return std::string("Retrieve the help string for a named method"); }
    240 };
    241 
    242 
    243 // Specify whether introspection is enabled or not. Default is enabled.
    244 void
    245 XmlRpcServer::enableIntrospection(bool enabled)
    246 {
    247   if (_introspectionEnabled == enabled)
    248     return;
    249 
    250   _introspectionEnabled = enabled;
    251 
    252   if (enabled)
    253   {
    254     if ( ! _listMethods)
    255     {
    256       _listMethods = new ListMethods(this);
    257       _methodHelp = new MethodHelp(this);
    258     } else {
    259       addMethod(_listMethods);
    260       addMethod(_methodHelp);
    261     }
    262   }
    263   else
    264   {
    265     removeMethod(LIST_METHODS);
    266     removeMethod(METHOD_HELP);
    267   }
    268 }
    269 
    270 
    271 void
    272 XmlRpcServer::listMethods(XmlRpcValue& result)
    273 {
    274   int i = 0;
    275   result.setSize(_methods.size()+1);
    276   for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
    277     result[i++] = it->first;
    278 
    279   // Multicall support is built into XmlRpcServerConnection
    280   result[i] = MULTICALL;
    281 }
    282 
    283 
    284 
    285