XmlRpcServer.cc

Go to the documentation of this file.
00001 
00007 #include "XmlRpcServer.h"
00008 #include "XmlRpcServerConnection.h"
00009 #include "XmlRpcServerMethod.h"
00010 #include "XmlRpcSocket.h"
00011 #include "XmlRpcUtil.h"
00012 #include "XmlRpcException.h"
00013 #include "XmlRpc.h"
00014 
00015 
00016 using namespace XmlRpc;
00017 
00018 
00019 // Static data
00020 const char XmlRpcServer::METHODNAME_TAG[] = "<methodName>";
00021 const char XmlRpcServer::PARAMS_TAG[] = "<params>";
00022 const char XmlRpcServer::PARAMS_ETAG[] = "</params>";
00023 const char XmlRpcServer::PARAM_TAG[] = "<param>";
00024 const char XmlRpcServer::PARAM_ETAG[] = "</param>";
00025 
00026 const std::string XmlRpcServer::METHODNAME = "methodName";
00027 const std::string XmlRpcServer::PARAMS = "params";
00028 
00029 const std::string XmlRpcServer::FAULTCODE = "faultCode";
00030 const std::string XmlRpcServer::FAULTSTRING = "faultString";
00031 
00032 
00033 
00034 XmlRpcServer::XmlRpcServer()
00035 {
00036   _introspectionEnabled = false;
00037   _listMethods = 0;
00038   _methodHelp = 0;
00039 }
00040 
00041 
00042 XmlRpcServer::~XmlRpcServer()
00043 {
00044   this->shutdown();
00045   _methods.clear();
00046   delete _listMethods;
00047   delete _methodHelp;
00048 }
00049 
00050 
00051 // Add a command to the RPC server
00052 void 
00053 XmlRpcServer::addMethod(XmlRpcServerMethod* method)
00054 {
00055   _methods[method->getName()] = method;
00056 }
00057 
00058 // Remove a command from the RPC server
00059 void 
00060 XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
00061 {
00062   MethodMap::iterator i = _methods.find(method->getName());
00063   if (i != _methods.end())
00064     _methods.erase(i);
00065 }
00066 
00067 // Remove a command from the RPC server by name
00068 void 
00069 XmlRpcServer::removeMethod(const std::string& methodName)
00070 {
00071   MethodMap::iterator i = _methods.find(methodName);
00072   if (i != _methods.end())
00073     _methods.erase(i);
00074 }
00075 
00076 
00077 // Look up a method by name
00078 XmlRpcServerMethod* 
00079 XmlRpcServer::findMethod(const std::string& name) const
00080 {
00081   MethodMap::const_iterator i = _methods.find(name);
00082   if (i == _methods.end())
00083     return 0;
00084   return i->second;
00085 }
00086 
00087 
00088 // Create a socket, bind to the specified port, and
00089 // set it in listen mode to make it available for clients.
00090 bool 
00091 XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
00092 {
00093   int fd = XmlRpcSocket::getSocket();
00094   if (fd < 0)
00095   {
00096     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
00097     return false;
00098   }
00099 
00100   this->setfd(fd);
00101 
00102   // Don't block on reads/writes
00103   if ( ! XmlRpcSocket::setNonBlocking(fd))
00104   {
00105     this->close();
00106     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00107     return false;
00108   }
00109 
00110   // Allow this port to be re-bound immediately so server re-starts are not delayed
00111   if ( ! XmlRpcSocket::setReuseAddr(fd))
00112   {
00113     this->close();
00114     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
00115     return false;
00116   }
00117 
00118   // Bind to the specified port on the default interface
00119   if ( ! XmlRpcSocket::bind(fd, port))
00120   {
00121     this->close();
00122     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
00123     return false;
00124   }
00125 
00126   // Set in listening mode
00127   if ( ! XmlRpcSocket::listen(fd, backlog))
00128   {
00129     this->close();
00130     XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00131     return false;
00132   }
00133 
00134   XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd);
00135 
00136   // Notify the dispatcher to listen on this source when we are in work()
00137   _disp.addSource(this, XmlRpcDispatch::ReadableEvent);
00138 
00139   return true;
00140 }
00141 
00142 
00143 // Get port number that this server is listening on
00144 int
00145 XmlRpcServer::getPort(void) const
00146 {
00147   return XmlRpcSocket::getPort(getfd());
00148 }
00149 
00150 
00151 
00152 // Process client requests for the specified time
00153 void 
00154 XmlRpcServer::work(double msTime)
00155 {
00156   XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
00157   _disp.work(msTime);
00158 }
00159 
00160 
00161 
00162 // Handle input on the server socket by accepting the connection
00163 // and reading the rpc request.
00164 unsigned
00165 XmlRpcServer::handleEvent(unsigned mask)
00166 {
00167   acceptConnection();
00168   return XmlRpcDispatch::ReadableEvent;         // Continue to monitor this fd
00169 }
00170 
00171 
00172 // Accept a client connection request and create a connection to
00173 // handle method calls from the client.
00174 void
00175 XmlRpcServer::acceptConnection()
00176 {
00177   int s = XmlRpcSocket::accept(this->getfd());
00178   XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
00179   if (s < 0)
00180   {
00181     //this->close();
00182     XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
00183   }
00184   else if ( ! XmlRpcSocket::setNonBlocking(s))
00185   {
00186     XmlRpcSocket::close(s);
00187     XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00188   }
00189   else  // Notify the dispatcher to listen for input on this source when we are in work()
00190   {
00191     XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
00192     XmlRpcServerConnection* c = this->createConnection(s);
00193     if (c) this->dispatchConnection(c);
00194   }
00195 }
00196 
00197 
00198 // Create a new connection object for processing requests from a specific client.
00199 XmlRpcServerConnection*
00200 XmlRpcServer::createConnection(int s)
00201 {
00202   // Specify that the connection object be deleted when it is closed
00203   return new XmlRpcServerConnection(s, this, true);
00204 }
00205 
00206 
00207 // Hand off a new connection to a dispatcher
00208 void
00209 XmlRpcServer::dispatchConnection(XmlRpcServerConnection* sc)
00210 {
00211   _disp.addSource(sc, XmlRpcDispatch::ReadableEvent);
00212 }
00213 
00214 
00215 // Remove a connection. Called by the connection when it closes down.
00216 void 
00217 XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
00218 {
00219   _disp.removeSource(sc);
00220 }
00221 
00222 
00223 // Stop processing client requests
00224 void 
00225 XmlRpcServer::exit()
00226 {
00227   _disp.exit();
00228 }
00229 
00230 
00231 // Close the server socket file descriptor and stop monitoring connections
00232 void 
00233 XmlRpcServer::shutdown()
00234 {
00235   // This closes and destroys all connections as well as closing this socket
00236   _disp.clear();
00237 }
00238 
00239 
00240 // Introspection support
00241 static const std::string LIST_METHODS("system.listMethods");
00242 static const std::string METHOD_HELP("system.methodHelp");
00243 static const std::string MULTICALL("system.multicall");
00244 
00245 
00246 // List all methods available on a server
00247 class ListMethods : public XmlRpcServerMethod
00248 {
00249 public:
00250   ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
00251 
00252   void execute(XmlRpcValue& params, XmlRpcValue& result)
00253   {
00254     _server->listMethods(result);
00255   }
00256 
00257   std::string help() { return std::string("List all methods available on a server as an array of strings"); }
00258 };
00259 
00260 
00261 // Retrieve the help string for a named method
00262 class MethodHelp : public XmlRpcServerMethod
00263 {
00264 public:
00265   MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
00266 
00267   void execute(XmlRpcValue& params, XmlRpcValue& result)
00268   {
00269     if (params[0].getType() != XmlRpcValue::TypeString)
00270       throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
00271 
00272     XmlRpcServerMethod* m = _server->findMethod(params[0]);
00273     if ( ! m)
00274       throw XmlRpcException(METHOD_HELP + ": Unknown method name");
00275 
00276     result = m->help();
00277   }
00278 
00279   std::string help() { return std::string("Retrieve the help string for a named method"); }
00280 };
00281 
00282     
00283 // Specify whether introspection is enabled or not. Default is enabled.
00284 void 
00285 XmlRpcServer::enableIntrospection(bool enabled)
00286 {
00287   if (_introspectionEnabled == enabled)
00288     return;
00289 
00290   _introspectionEnabled = enabled;
00291 
00292   if (enabled)
00293   {
00294     if ( ! _listMethods)
00295     {
00296       _listMethods = new ListMethods(this);
00297       _methodHelp = new MethodHelp(this);
00298     } else {
00299       addMethod(_listMethods);
00300       addMethod(_methodHelp);
00301     }
00302   }
00303   else
00304   {
00305     removeMethod(LIST_METHODS);
00306     removeMethod(METHOD_HELP);
00307   }
00308 }
00309 
00310 
00311 void
00312 XmlRpcServer::listMethods(XmlRpcValue& result)
00313 {
00314   int i = 0;
00315   result.setSize(_methods.size()+1);
00316   for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
00317     result[i++] = it->first;
00318 
00319   // Multicall support is built into XmlRpcServer::executeRequest
00320   result[i] = MULTICALL;
00321 }
00322 
00323 
00324 
00325 // Parse the request, run the method, generate a response string.
00326 std::string
00327 XmlRpcServer::executeRequest(std::string const& request)
00328 {
00329   XmlRpcValue params, resultValue;
00330   std::string methodName = parseRequest(request, params);
00331   XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: server calling method '%s'", 
00332                     methodName.c_str());
00333 
00334   std::string response;
00335   try {
00336 
00337     if ( ! executeMethod(methodName, params, resultValue) &&
00338          ! executeMulticall(methodName, params, resultValue))
00339       response = generateFaultResponse(methodName + ": unknown method name");
00340     else
00341       response = generateResponse(resultValue.toXml());
00342 
00343   } catch (const XmlRpcException& fault) {
00344     XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: fault %s.",
00345                     fault.getMessage().c_str()); 
00346     response = generateFaultResponse(fault.getMessage(), fault.getCode());
00347   }
00348 
00349   return response;
00350 }
00351 
00352 // Parse the method name and the argument values from the request.
00353 std::string
00354 XmlRpcServer::parseRequest(std::string const& request, XmlRpcValue& params)
00355 {
00356   int offset = 0;   // Number of chars parsed from the request
00357 
00358   std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, request, &offset);
00359 
00360   if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, request, &offset))
00361   {
00362     int nArgs = 0;
00363     while (XmlRpcUtil::nextTagIs(PARAM_TAG, request, &offset)) {
00364       params[nArgs++] = XmlRpcValue(request, &offset);
00365       (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, request, &offset);
00366     }
00367 
00368     (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, request, &offset);
00369   }
00370 
00371   return methodName;
00372 }
00373 
00374 // Execute a named method with the specified params.
00375 bool
00376 XmlRpcServer::executeMethod(const std::string& methodName, 
00377                             XmlRpcValue& params, 
00378                             XmlRpcValue& result)
00379 {
00380   XmlRpcServerMethod* method = findMethod(methodName);
00381 
00382   if ( ! method) return false;
00383 
00384   method->execute(params, result);
00385 
00386   // Ensure a valid result value
00387   if ( ! result.isValid())
00388       result = std::string();
00389 
00390   return true;
00391 }
00392 
00393 // Execute multiple calls and return the results in an array.
00394 bool
00395 XmlRpcServer::executeMulticall(const std::string& methodName, 
00396                                XmlRpcValue& params, 
00397                                XmlRpcValue& result)
00398 {
00399   if (methodName != MULTICALL) return false;
00400 
00401   // There ought to be 1 parameter, an array of structs
00402   if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
00403     throw XmlRpcException(MULTICALL + ": Invalid argument (expected an array)");
00404 
00405   int nc = params[0].size();
00406   result.setSize(nc);
00407 
00408   for (int i=0; i<nc; ++i) {
00409 
00410     if ( ! params[0][i].hasMember(METHODNAME) ||
00411          ! params[0][i].hasMember(PARAMS)) {
00412       result[i][FAULTCODE] = -1;
00413       result[i][FAULTSTRING] = MULTICALL +
00414               ": Invalid argument (expected a struct with members methodName and params)";
00415       continue;
00416     }
00417 
00418     const std::string& methodName = params[0][i][METHODNAME];
00419     XmlRpcValue& methodParams = params[0][i][PARAMS];
00420 
00421     XmlRpcValue resultValue;
00422     resultValue.setSize(1);
00423     try {
00424       if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
00425            ! executeMulticall(methodName, params, resultValue[0]))
00426       {
00427         result[i][FAULTCODE] = -1;
00428         result[i][FAULTSTRING] = methodName + ": unknown method name";
00429       }
00430       else
00431         result[i] = resultValue;
00432 
00433     } catch (const XmlRpcException& fault) {
00434         result[i][FAULTCODE] = fault.getCode();
00435         result[i][FAULTSTRING] = fault.getMessage();
00436     }
00437   }
00438 
00439   return true;
00440 }
00441 
00442 
00443 // Create a response from results xml
00444 std::string
00445 XmlRpcServer::generateResponse(std::string const& resultXml)
00446 {
00447   const char RESPONSE_1[] = 
00448     "<?xml version=\"1.0\"?>\r\n"
00449     "<methodResponse><params><param>\r\n\t";
00450   const char RESPONSE_2[] =
00451     "\r\n</param></params></methodResponse>\r\n";
00452 
00453   std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
00454   std::string header = generateHeader(body);
00455   std::string response = header + body;
00456 
00457   XmlRpcUtil::log(5, "XmlRpcServer::generateResponse:\n%s\n", response.c_str());
00458   return response;
00459 }
00460 
00461 
00462 // Prepend http headers
00463 std::string
00464 XmlRpcServer::generateHeader(std::string const& body)
00465 {
00466   std::string header = 
00467     "HTTP/1.1 200 OK\r\n"
00468     "Server: ";
00469   header += XMLRPC_VERSION;
00470   header += "\r\n"
00471     "Content-Type: text/xml\r\n"
00472     "Content-length: ";
00473 
00474   char buffLen[40];
00475   sprintf(buffLen,"%d\r\n\r\n", body.size());
00476 
00477   return header + buffLen;
00478 }
00479 
00480 
00481 std::string
00482 XmlRpcServer::generateFaultResponse(std::string const& errorMsg, int errorCode)
00483 {
00484   const char RESPONSE_1[] = 
00485     "<?xml version=\"1.0\"?>\r\n"
00486     "<methodResponse><fault>\r\n\t";
00487   const char RESPONSE_2[] =
00488     "\r\n</fault></methodResponse>\r\n";
00489 
00490   XmlRpcValue faultStruct;
00491   faultStruct[FAULTCODE] = errorCode;
00492   faultStruct[FAULTSTRING] = errorMsg;
00493   std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
00494   std::string header = generateHeader(body);
00495 
00496   return header + body;
00497 }
00498 
Generated on Wed May 26 16:21:15 2010 for OverSim by  doxygen 1.6.3