$search
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