XmlRpcServer.cpp
Go to the documentation of this file.
00001 // this file modified by Morgan Quigley on 22 Apr 2008.
00002 // added features: server can be opened on port 0 and you can read back
00003 // what port the OS gave you
00004 
00005 #include "XmlRpcServer.h"
00006 #include "XmlRpcServerConnection.h"
00007 #include "XmlRpcServerMethod.h"
00008 #include "XmlRpcSocket.h"
00009 #include "XmlRpcUtil.h"
00010 #include "XmlRpcException.h"
00011 
00012 
00013 using namespace XmlRpc;
00014 
00015 
00016 XmlRpcServer::XmlRpcServer()
00017 {
00018   _introspectionEnabled = false;
00019   _listMethods = 0;
00020   _methodHelp = 0;
00021 }
00022 
00023 
00024 XmlRpcServer::~XmlRpcServer()
00025 {
00026   this->shutdown();
00027   _methods.clear();
00028   delete _listMethods;
00029   delete _methodHelp;
00030 }
00031 
00032 
00033 // Add a command to the RPC server
00034 void 
00035 XmlRpcServer::addMethod(XmlRpcServerMethod* method)
00036 {
00037   _methods[method->name()] = method;
00038 }
00039 
00040 // Remove a command from the RPC server
00041 void 
00042 XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
00043 {
00044   MethodMap::iterator i = _methods.find(method->name());
00045   if (i != _methods.end())
00046     _methods.erase(i);
00047 }
00048 
00049 // Remove a command from the RPC server by name
00050 void 
00051 XmlRpcServer::removeMethod(const std::string& methodName)
00052 {
00053   MethodMap::iterator i = _methods.find(methodName);
00054   if (i != _methods.end())
00055     _methods.erase(i);
00056 }
00057 
00058 
00059 // Look up a method by name
00060 XmlRpcServerMethod* 
00061 XmlRpcServer::findMethod(const std::string& name) const
00062 {
00063   MethodMap::const_iterator i = _methods.find(name);
00064   if (i == _methods.end())
00065     return 0;
00066   return i->second;
00067 }
00068 
00069 
00070 // Create a socket, bind to the specified port, and
00071 // set it in listen mode to make it available for clients.
00072 bool 
00073 XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
00074 {
00075   int fd = XmlRpcSocket::socket();
00076   if (fd < 0)
00077   {
00078     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
00079     return false;
00080   }
00081 
00082   this->setfd(fd);
00083 
00084   // Don't block on reads/writes
00085   if ( ! XmlRpcSocket::setNonBlocking(fd))
00086   {
00087     this->close();
00088     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00089     return false;
00090   }
00091 
00092   // Allow this port to be re-bound immediately so server re-starts are not delayed
00093   if ( ! XmlRpcSocket::setReuseAddr(fd))
00094   {
00095     this->close();
00096     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
00097     return false;
00098   }
00099 
00100   // Bind to the specified port on the default interface
00101   if ( ! XmlRpcSocket::bind(fd, port))
00102   {
00103     this->close();
00104     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
00105     return false;
00106   }
00107 
00108   // Set in listening mode
00109   if ( ! XmlRpcSocket::listen(fd, backlog))
00110   {
00111     this->close();
00112     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00113     return false;
00114   }
00115 
00116   _port = XmlRpcSocket::get_port(fd);
00117 
00118   XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", _port, fd);
00119 
00120   // Notify the dispatcher to listen on this source when we are in work()
00121   _disp.addSource(this, XmlRpcDispatch::ReadableEvent);
00122 
00123   return true;
00124 }
00125 
00126 
00127 // Process client requests for the specified time
00128 void 
00129 XmlRpcServer::work(double msTime)
00130 {
00131   XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
00132   _disp.work(msTime);
00133 }
00134 
00135 
00136 
00137 // Handle input on the server socket by accepting the connection
00138 // and reading the rpc request.
00139 unsigned
00140 XmlRpcServer::handleEvent(unsigned)
00141 {
00142   acceptConnection();
00143   return XmlRpcDispatch::ReadableEvent;         // Continue to monitor this fd
00144 }
00145 
00146 
00147 // Accept a client connection request and create a connection to
00148 // handle method calls from the client.
00149 void
00150 XmlRpcServer::acceptConnection()
00151 {
00152   int s = XmlRpcSocket::accept(this->getfd());
00153   XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
00154   if (s < 0)
00155   {
00156     //this->close();
00157     XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
00158   }
00159   else if ( ! XmlRpcSocket::setNonBlocking(s))
00160   {
00161     XmlRpcSocket::close(s);
00162     XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00163   }
00164   else  // Notify the dispatcher to listen for input on this source when we are in work()
00165   {
00166     XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
00167     _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent);
00168   }
00169 }
00170 
00171 
00172 // Create a new connection object for processing requests from a specific client.
00173 XmlRpcServerConnection*
00174 XmlRpcServer::createConnection(int s)
00175 {
00176   // Specify that the connection object be deleted when it is closed
00177   return new XmlRpcServerConnection(s, this, true);
00178 }
00179 
00180 
00181 void 
00182 XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
00183 {
00184   _disp.removeSource(sc);
00185 }
00186 
00187 
00188 // Stop processing client requests
00189 void 
00190 XmlRpcServer::exit()
00191 {
00192   _disp.exit();
00193 }
00194 
00195 
00196 // Close the server socket file descriptor and stop monitoring connections
00197 void 
00198 XmlRpcServer::shutdown()
00199 {
00200   // This closes and destroys all connections as well as closing this socket
00201   _disp.clear();
00202 }
00203 
00204 
00205 // Introspection support
00206 static const std::string LIST_METHODS("system.listMethods");
00207 static const std::string METHOD_HELP("system.methodHelp");
00208 static const std::string MULTICALL("system.multicall");
00209 
00210 
00211 // List all methods available on a server
00212 class ListMethods : public XmlRpcServerMethod
00213 {
00214 public:
00215   ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
00216 
00217   void execute(XmlRpcValue&, XmlRpcValue& result)
00218   {
00219     _server->listMethods(result);
00220   }
00221 
00222   std::string help() { return std::string("List all methods available on a server as an array of strings"); }
00223 };
00224 
00225 
00226 // Retrieve the help string for a named method
00227 class MethodHelp : public XmlRpcServerMethod
00228 {
00229 public:
00230   MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
00231 
00232   void execute(XmlRpcValue& params, XmlRpcValue& result)
00233   {
00234     if (params[0].getType() != XmlRpcValue::TypeString)
00235       throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
00236 
00237     XmlRpcServerMethod* m = _server->findMethod(params[0]);
00238     if ( ! m)
00239       throw XmlRpcException(METHOD_HELP + ": Unknown method name");
00240 
00241     result = m->help();
00242   }
00243 
00244   std::string help() { return std::string("Retrieve the help string for a named method"); }
00245 };
00246 
00247     
00248 // Specify whether introspection is enabled or not. Default is enabled.
00249 void 
00250 XmlRpcServer::enableIntrospection(bool enabled)
00251 {
00252   if (_introspectionEnabled == enabled)
00253     return;
00254 
00255   _introspectionEnabled = enabled;
00256 
00257   if (enabled)
00258   {
00259     if ( ! _listMethods)
00260     {
00261       _listMethods = new ListMethods(this);
00262       _methodHelp = new MethodHelp(this);
00263     } else {
00264       addMethod(_listMethods);
00265       addMethod(_methodHelp);
00266     }
00267   }
00268   else
00269   {
00270     removeMethod(LIST_METHODS);
00271     removeMethod(METHOD_HELP);
00272   }
00273 }
00274 
00275 
00276 void
00277 XmlRpcServer::listMethods(XmlRpcValue& result)
00278 {
00279   int i = 0;
00280   result.setSize(_methods.size()+1);
00281   for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
00282     result[i++] = it->first;
00283 
00284   // Multicall support is built into XmlRpcServerConnection
00285   result[i] = MULTICALL;
00286 }
00287 
00288 
00289 


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley
autogenerated on Mon Oct 6 2014 11:46:40